diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7654d829c4..26d6515b04 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,12 +26,16 @@ + + + + - + @@ -70,9 +74,9 @@ android:icon="@mipmap/logo" android:label="@string/app_name" android:largeHeap="true" + android:networkSecurityConfig="@xml/network_security_config" android:resizeableActivity="true" android:theme="@style/AppCompatTheme.APP" - android:networkSecurityConfig="@xml/network_security_config" tools:replace="android:allowBackup" tools:targetApi="n"> @@ -475,7 +479,7 @@ android:screenOrientation="portrait" /> + + - - @@ -585,10 +591,6 @@ android:screenOrientation="portrait" android:theme="@style/TransparentStatusBarAndNavigationBar" /> - - @@ -611,8 +613,8 @@ + android:launchMode="singleTask" + android:screenOrientation="portrait" /> + + + + + + + + + + + + + + + + + + + + - + android:theme="@android:style/Theme.Dialog" /> : ToolBarActivity(), KeyboardHeightObserver, UploadVideoListener { val mRichEditor by bindView(R.id.rich_editor) @@ -55,21 +62,21 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { private val mEditorParagraphContainer by bindView(R.id.editor_paragraph_container) private val mEditorLinkContainer by bindView(R.id.editor_link_container) private val mEditorInsertDetailContainer by bindView(R.id.editor_insert_detail_container) - val mAddLabelContainer by bindView(R.id.add_label_container) - val mAddLabelTv by bindView(R.id.add_label_tv) - val mLabelNumTv by bindView(R.id.label_num_tv) - val mLabelArrowIv by bindView(R.id.label_arrow) - val mTagsContainer by bindView(R.id.tagsContainer) + private val mTagsContainer by bindView(R.id.tagsContainer) private var mCurrentParagraphStyle = "" private var mIsExtendedKeyboardShow = false - + private var mAgreePostPic: Boolean = false + protected lateinit var mViewModel: VM + protected var mIsKeyBoardShow = false + protected var mKeyboardHeightProvider: KeyboardHeightProvider? = null + 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 - var insertData: EditorInsertEntity? = null + val insertData: EditorInsertEntity? when (requestCode) { INSERT_ANSWER_CODE -> { val answer = data?.getParcelableExtra(AnswerEntity::class.java.simpleName) @@ -95,11 +102,20 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { mRichEditor.insertCustomStyleLink(insertData) } } - VideoActivity.INSERT_VIDEO_CODE -> { - val video = data?.getParcelableExtra(MyVideoEntity::class.java.simpleName) - if (video != null) { - mRichEditor.focusEditor() - mRichEditor.insertCustomVideo(video) + REQUEST_CODE_IMAGE -> { + if (data != null) mViewModel.uploadPic(data) + } + INSERT_VIDEO_CODE -> { + val localVideoList = data?.getParcelableArrayListExtra(LocalVideoEntity::class.java.name) + ?: arrayListOf() + if (localVideoList.isNotEmpty()) { + uploadVideo(localVideoList) + } + } + REQUEST_CODE_IMAGE_CROP -> { + val imagePath = data?.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) + if (!imagePath.isNullOrEmpty()) { + mViewModel.uploadPoster(imagePath) } } } @@ -107,19 +123,48 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { 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() + } } @SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mAddLabelContainer.visibility = if (this is ArticleEditActivity) View.VISIBLE else View.GONE + mViewModel = provideViewModel() + mViewModel.setUploadVideoListener(this) + mKeyboardHeightProvider = KeyboardHeightProvider(this) + mRichEditor.post { mKeyboardHeightProvider?.start() } mRichEditor.setPadding(20, 15, 20, 15) // 防止个别手机在Js里无法获取粘贴内容 mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener") mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener") mRichEditor.addJavascriptInterface(OnEditorTextChangeListener(), "OnEditorTextChangeListener") + mRichEditor.addJavascriptInterface(OnVideoListener(), "onVideoListener") mRichEditor.setInputEnabled(true) + mRichEditor.setPadding(16, 15, 16, 15) mRichEditor.setOnTouchListener { _, _ -> if (mIsExtendedKeyboardShow) { @@ -129,14 +174,41 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { mRichEditor.hasFocus() } else false } + observeData() } + private fun observeData() { + mViewModel.chooseImagesUpload.observe(this, Observer { + 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()) + AppExecutor.uiExecutor.executeWithDelay(Runnable { + Util_System_Keyboard.showSoftKeyboard(this) + }, 100) + }) + } + + 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 - mAddLabelContainer.isSelected = false mIsExtendedKeyboardShow = false } @@ -144,36 +216,7 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { mEditorFont.isEnabled = isEnabled } - fun changeAddLabel(isLabelContainerShow: Boolean) { - if (isLabelContainerShow) { - mLabelNumTv.visibility = View.GONE - mLabelArrowIv.visibility = View.VISIBLE - mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font)) - mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - mAddLabelContainer.background = ContextCompat.getDrawable(this, R.drawable.bg_editor_insert_add_label) - } else { - val selectedLabel = getSelectedLabel() - if (selectedLabel == 0) { - mAddLabelTv.text = "添加标签" - mLabelNumTv.visibility = View.GONE - mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.text_666666)) - mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(this, R.drawable.ic_add_label), null, null, null) - } else { - mAddLabelTv.text = "标签" - mLabelNumTv.visibility = View.VISIBLE - mLabelNumTv.text = selectedLabel.toString() - mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font)) - mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - } - mLabelArrowIv.visibility = View.GONE - mAddLabelContainer.background = ContextCompat.getDrawable(this, R.drawable.border_round_stroke_eee_999) - } - } - - open fun getSelectedLabel(): Int = 0 - - - @OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.add_label_container, R.id.editor_font_underline, + @OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.editor_font_underline, R.id.editor_font_bold, R.id.editor_font_italic, R.id.editor_font_strikethrough, R.id.editor_paragraph_h1, R.id.editor_paragraph_h2, R.id.editor_paragraph_h3, R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container, @@ -187,9 +230,6 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { R.id.editor_link -> { controlEditorLinkContainer() } - R.id.add_label_container -> { - controlAddLabelContainer() - } R.id.editor_font_bold -> { mEditorFontBold.isChecked = !mEditorFontBold.isChecked mRichEditor.setBold() @@ -282,17 +322,46 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { object : EmptyCallback { override fun onCallback() { MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频") - startActivityForResult(VideoActivity.getIntent(this@BaseRichEditorActivity), VideoActivity.INSERT_VIDEO_CODE) + startActivityForResult(LocalMediaActivity.getIntent(this@BaseRichEditorActivity, LocalMediaActivity.ChooseType.VIDEO, 3), INSERT_VIDEO_CODE) } }) } + R.id.editor_image -> { + if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) { + mAgreePostPic = true + DialogUtils.showAlertDialog(this, + "警告", + "当前使用移动网络,上传图片会消耗手机流量", + "我知道了", "", { startMediaStore() }, null) + return + } + startMediaStore() + } + } + } + + private fun startMediaStore() { + MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片") + if (mViewModel.mapImages.size >= 50) { + toast(R.string.answer_edit_max_img_hint) + return + } + try { + PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback { + override fun onCallback() { + val intent = LocalMediaActivity.getIntent(this@BaseRichEditorActivity, LocalMediaActivity.ChooseType.IMAGE, 10) + 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 - mAddLabelContainer.isSelected = false val isShouldDelay = if (mEditorFont.isChecked) { Util_System_Keyboard.hideSoftKeyboard(this) true @@ -307,14 +376,12 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { mEditorLinkContainer.visibility = View.GONE mTagsContainer.visibility = View.GONE mIsExtendedKeyboardShow = mEditorFont.isChecked - changeAddLabel(false) }, if (isShouldDelay) 200 else 0L) } private fun controlEditorLinkContainer() { mEditorLink.isChecked = !mEditorLink.isChecked mEditorFont.isChecked = false - mAddLabelContainer.isSelected = false val isShouldDelay = if (mEditorLink.isChecked) { Util_System_Keyboard.hideSoftKeyboard(this) true @@ -329,32 +396,9 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { mEditorParagraphContainer.visibility = View.GONE mTagsContainer.visibility = View.GONE mIsExtendedKeyboardShow = mEditorLink.isChecked - changeAddLabel(false) }, if (isShouldDelay) 200 else 0L) } - fun controlAddLabelContainer() { - mEditorLink.isChecked = false - mEditorFont.isChecked = false - mAddLabelContainer.isSelected = !mAddLabelContainer.isSelected - val isShouldDelay = if (mAddLabelContainer.isSelected) { - Util_System_Keyboard.hideSoftKeyboard(this) - changeAddLabel(true) - true - } else { - Util_System_Keyboard.showSoftKeyboard(this) - changeAddLabel(false) - false - } - mEditorInsertDetailContainer.postDelayed({ - mEditorInsertDetailContainer.visibility = if (mAddLabelContainer.isSelected) View.VISIBLE else View.GONE - mTagsContainer.visibility = if (mAddLabelContainer.isSelected) View.VISIBLE else View.GONE - mEditorLinkContainer.visibility = View.GONE - mEditorFontContainer.visibility = View.GONE - mEditorParagraphContainer.visibility = View.GONE - mIsExtendedKeyboardShow = mAddLabelContainer.isSelected - }, if (isShouldDelay) 200 else 0L) - } override fun handleBackPressed(): Boolean { if (mIsExtendedKeyboardShow) { @@ -364,6 +408,24 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { return super.handleBackPressed() } + override fun onResume() { + super.onResume() + mKeyboardHeightProvider?.setKeyboardHeightObserver(this) + } + + override fun onPause() { + super.onPause() + mKeyboardHeightProvider?.setKeyboardHeightObserver(null) + } + + override fun onDestroy() { + super.onDestroy() + mKeyboardHeightProvider?.close() + val path = mViewModel.currentUploadingVideo?.filePath + if (path != null && UploadManager.isUploading(path)) { + UploadManager.cancelTask(path) + } + } private inner class OnCursorChangeListener { @JavascriptInterface @@ -417,7 +479,75 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { } } + 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 / 1000)) + 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 abstract fun mtaEventName(): String + abstract fun provideViewModel(): VM companion object { const val ELEMENT_NAME_BOLD = " b " @@ -434,5 +564,9 @@ abstract class BaseRichEditorActivity : ToolBarActivity() { const val INSERT_ARTICLE_CODE = 412 const val INSERT_GAME_CODE = 413 const val MAX_INPUT_TEXT_NUM = 10000 + + const val REQUEST_CODE_IMAGE = 120 + const val INSERT_VIDEO_CODE = 121 + const val REQUEST_CODE_IMAGE_CROP = 122 } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/base/BaseRichEditorViewModel.kt b/app/src/main/java/com/gh/base/BaseRichEditorViewModel.kt new file mode 100644 index 0000000000..b5720bb6d1 --- /dev/null +++ b/app/src/main/java/com/gh/base/BaseRichEditorViewModel.kt @@ -0,0 +1,349 @@ +package com.gh.base + +import android.app.Application +import android.content.Intent +import android.text.TextUtils +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import com.gh.base.fragment.WaitingDialogFragment +import com.gh.common.runOnUiThread +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.entity.ErrorEntity +import com.gh.gamecenter.entity.LocalVideoEntity +import com.gh.gamecenter.qa.BbsType +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.retrofit.service.ApiService +import com.gh.gamecenter.video.upload.OnUploadListener +import com.gh.gamecenter.video.upload.UploadManager +import com.google.gson.JsonObject +import com.lightgame.utils.Utils +import com.zhihu.matisse.Matisse +import com.zhihu.matisse.internal.utils.PathUtils +import io.reactivex.disposables.Disposable +import okhttp3.ResponseBody +import retrofit2.HttpException +import java.io.File +import java.util.* +import kotlin.collections.HashMap +import kotlin.collections.List +import kotlin.collections.Map +import kotlin.collections.find +import kotlin.collections.forEach +import kotlin.collections.set + +abstract class BaseRichEditorViewModel(application: Application) : AndroidViewModel(application) { + val mApi: ApiService = RetrofitManager.getInstance(application).api + val processDialog = MediatorLiveData() + val uploadingImage = ArrayList>() + val chooseImagesUpload = MutableLiveData>() + val chooseImagesUploadSuccess = MutableLiveData>() + var uploadImageSubscription: Disposable? = null + val mapImages = HashMap() + val localVideoList = ArrayList() + val uploadVideoErrorList = ArrayList() + var currentUploadingVideo: LocalVideoEntity? = null + var type: String = "" //游戏论坛:game_bbs 官方论坛:official_bbs + private var mUploadVideoListener: UploadVideoListener? = null + val TITLE_MIN_LENGTH = 6 + val MIN_TEXT_LENGTH = 6 + val MAX_TEXT_LENGTH = 10000 + val FILE_HOST = "file:///" + var id = ""//视频标记 + var videoId = ""//更改封面视频id + + fun setUploadVideoListener(uploadVideoListener: UploadVideoListener) { + this.mUploadVideoListener = uploadVideoListener + } + + //检查图片是否符合规则并上传图片 + fun uploadPic(data: Intent) { + val uris = Matisse.obtainResult(data) + val pictureList = ArrayList() + for (uri in uris) { + val picturePath = PathUtils.getPath(getApplication(), uri) + if (picturePath != null) { + if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) { + val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024 + val application: Application = getApplication() + Utils.toast(getApplication(), application.getString(R.string.pic_max_hint, count)) + continue + } + Utils.log("picturePath = $picturePath") + pictureList.add(picturePath) + } else { + Utils.log("picturePath is null") + } + } + if (pictureList.size == 0) return + + uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(UploadImageUtils.UploadType.answer, pictureList + , false, object : UploadImageUtils.OnUploadImageListListener { + override fun onProgress(total: Long, progress: Long) {} + + override fun onCompressSuccess(imageUrls: List) { + val chooseImageMd5Map = LinkedHashMap() + imageUrls.forEach { + chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = "" + } + uploadingImage.add(chooseImageMd5Map) + chooseImagesUpload.postValue(chooseImageMd5Map) + } + + override fun onSuccess(imageUrl: LinkedHashMap, errorMap: Map) { + val uploadMap = uploadingImage.find { it.containsKey(MD5Utils.getUrlMD5(imageUrl.entries.iterator().next().key)) } + uploadMap?.let { + for (key in imageUrl.keys) { + uploadMap[MD5Utils.getUrlMD5(key)] = FILE_HOST + key.decodeURI() + mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: "" + } + chooseImagesUploadSuccess.postValue(uploadMap) + uploadingImage.remove(uploadMap) + } + val errorSize = pictureList.size - imageUrl.size + if (errorSize > 0) { + for (error in errorMap.values) { + if (error is HttpException && error.code() == 403) { + Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败") + return + } + } + Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败") + } + } + + override fun onError(errorMap: Map) { + val errorSize = pictureList.size + + for (error in errorMap.values) { + if (error is HttpException && error.code() == 403) { + val e = error.response()?.errorBody()?.string()?.toObject() + if (e != null && e.code == 403017) { + Utils.toast(getApplication(), errorSize.toString() + "张图片的宽或高超过限制,请裁剪后上传") + } else { + Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败") + } + return + } + } + if (errorSize == 1) { + Utils.toast(getApplication(), "图片上传失败") + } else { + Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败") + } + } + }) + } + + fun uploadPoster(picturePath: String) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("图片上传中...", true)) + uploadImageSubscription = UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster, picturePath, false, + object : UploadImageUtils.OnUploadImageListener { + override fun onSuccess(imageUrl: String) { + patchVideoPoster(imageUrl) + } + + override fun onError(e: Throwable?) { + handleUploadPosterResult(true) + } + + override fun onProgress(total: Long, progress: Long) { + + } + }) + } + + private fun patchVideoPoster(poster: String) { + if (id.isEmpty() || videoId.isEmpty()) return + val map = hashMapOf("poster" to poster) + mApi.patchBbsVideo(videoId, map.toRequestBody()) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + mUploadVideoListener?.changePoster(id, poster) + handleUploadPosterResult(false) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + handleUploadPosterResult(true) + } + }) + } + + private fun handleUploadPosterResult(isFailure: Boolean = false) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("图片上传中...", false)) + if (isFailure) { + ToastUtils.showToast("封面更改失败") + } + id = "" + videoId = "" + } + + fun deleteVideo(id: String) { + if (localVideoList.isNotEmpty()) { + val video = localVideoList.find { it.id == id } + if (video != null) { + if (UploadManager.isUploading(video.filePath)) { + UploadManager.cancelTask(video.filePath) + } + localVideoList.remove(video) + } + } + if (uploadVideoErrorList.isNotEmpty()) { + val video = uploadVideoErrorList.find { it.id == id } + if (video != null) { + uploadVideoErrorList.remove(video) + } + } + if (currentUploadingVideo?.id == id) { + currentUploadingVideo = null + uploadVideo() + } + } + + fun uploadVideo() { + if (currentUploadingVideo != null) return + if (localVideoList.isEmpty()) return + currentUploadingVideo = localVideoList[0] + UploadManager.createUploadTask(currentUploadingVideo?.filePath + ?: "", object : OnUploadListener { + override fun onProgressChanged(uploadFilePath: String, currentSize: Long, totalSize: Long, speed: Long) { + runOnUiThread { + val percent = (currentSize * 100 / totalSize.toFloat()).roundTo(1) + currentUploadingVideo?.id?.let { + mUploadVideoListener?.updateVideoProgress(it, percent.toString()) + } + } + } + + override fun onUploadSuccess(uploadFilePath: String, url: String) { + if (currentUploadingVideo != null) { + val newPoster = ImageUtils.getVideoSnapshot(url, 0) + postVideoInfo(url, newPoster) + } + } + + override fun onUploadFailure(uploadFilePath: String, errorMsg: String) { + uploadFailure() + } + }) + } + + private fun getVideoType(): String { + return when (type) { + BbsType.GAME_BBS.value -> { + when (getRichType()) { + RichType.ARTICLE -> BbsType.GAME_BBS_ARTICLE_INSERT.value + RichType.QUESTION -> BbsType.GAME_BBS_QUESTION_INSERT.value + else -> "" + } + } + BbsType.OFFICIAL_BBS.value -> { + when (getRichType()) { + RichType.ARTICLE -> BbsType.OFFICIAL_BBS_ARTICLE_INSERT.value + RichType.QUESTION -> BbsType.OFFICIAL_BBS_QUESTION_INSERT.value + else -> "" + } + } + else -> "" + } + } + + fun postVideoInfo(url: String, poster: String) { + val map = HashMap().apply { + put("poster", poster) + put("url", url) + put("format", currentUploadingVideo?.format ?: "") + put("size", currentUploadingVideo?.size ?: 0) + put("length", currentUploadingVideo?.duration ?: 0) + put("type", getVideoType()) + } + val requestBody = map.toRequestBody() + mApi.postBbsVideo(requestBody) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: JsonObject?) { + super.onResponse(response) + if (response != null) { + uploadSuccess(poster, url, response) + } + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + uploadFailure() + } + }) + } + + private fun uploadSuccess(poster: String, url: String, data: JsonObject) { + currentUploadingVideo?.let { + mUploadVideoListener?.changePoster(it.id, poster) + mUploadVideoListener?.videoUploadFinished(it.id, url, data) + UploadManager.cancelTask(it.filePath) + localVideoList.remove(it) + } + currentUploadingVideo = null + uploadVideo() + } + + private fun uploadFailure() { + currentUploadingVideo?.let { + runOnUiThread { + mUploadVideoListener?.videoUploadFailed(it.id) + } + uploadVideoErrorList.add(it) + localVideoList.remove(it) + UploadManager.cancelTask(it.filePath) + } + currentUploadingVideo = null + uploadVideo() + } + + fun checkIsAllUploadedAndToast(): Boolean { + if (localVideoList.isNotEmpty() || uploadVideoErrorList.isNotEmpty()) { + ToastUtils.showToast("视频未上传完成,视频内容保存失败") + return false + } + return true + } + + abstract fun getRichType(): RichType +} + +interface UploadVideoListener { + /** + * 插入视频占位图 + */ + fun insertPlaceholderVideo(id: String, poster: String) + + /** + * 更新视频进度条 + */ + fun updateVideoProgress(id: String, progress: String) + + /** + * 上传视频完成 + */ + fun videoUploadFinished(id: String, url: String, msg: JsonObject) + + /** + * 更换封面图 + */ + fun changePoster(id: String, poster: String) + + /** + * 上传失败 + */ + fun videoUploadFailed(id: String) +} + +enum class RichType { + ARTICLE, + QUESTION, + ANSWER +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/constant/Constants.java b/app/src/main/java/com/gh/common/constant/Constants.java index 485f734ece..79a994615f 100644 --- a/app/src/main/java/com/gh/common/constant/Constants.java +++ b/app/src/main/java/com/gh/common/constant/Constants.java @@ -1,6 +1,7 @@ package com.gh.common.constant; import com.gh.common.util.PackageUtils; +import com.gh.common.util.TimeUtils; import com.halo.assistant.HaloApp; public class Constants { @@ -170,6 +171,9 @@ public class Constants { //首页视频播放进度 public static final String SP_HOME_VIDEO_PLAY_RECORD = "home_video_play_record"; + // 论坛内容视频播放进度 + public static final String SP_CONTENT_VIDEO_PLAY_RECORD = "content_video_play_record"; + // 用户是否曾经永久拒绝过存储权限 public static final String SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION = "user_has_permanently_denied_storage_permission"; @@ -193,6 +197,19 @@ public class Constants { // 是否成功取过号 public static final String SP_HAS_GET_PHONE_INFO = "has_get_phone_info"; + // 是否点击过更换背景按钮 + public static final String SP_HAS_CLICK_CHANGE_BG = "has_click_change_bg"; + // 是否显示更换背景提示 + public static final String SP_SHOW_CHANGE_BG_TIPS = "show_change_bg_tips" + TimeUtils.getStartTimeOfToday(); + + + // 内容视频播放选项 + public static final String SP_CONTENT_VIDEO_OPTION = "content_video_option"; + // 首页/游戏详情页视频播放选项 + public static final String SP_HOME_OR_DETAIL_VIDEO_OPTION = "home_or_detail_video_option"; + // 是否默认静音播放视频 + public static final String SP_VIDEO_PLAY_MUTE = "video_play_mute"; + //手机号码匹配规则 public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"; public static final String REGEX_ACCOUNT = "^[a-zA-Z_]\\w{5,17}$"; @@ -298,6 +315,10 @@ public class Constants { public static final String WITHDRAW_INFO_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/cash?from=ghzs"; public static final String WITHDRAW_INFO_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/cash?from=ghzs"; + // 活动详情 + public static final String ACTIVITY_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_activity_dev/common.html?from=ghzs"; + public static final String ACTIVITY_DETAIL_ADDRESS = "https://static-web.ghzs.com/ghzs_activity_prod/common.html?from=ghzs"; + //最少需要多少数据才能上传 public static final int DATA_AMOUNT = 20; diff --git a/app/src/main/java/com/gh/common/databind/BindingAdapters.java b/app/src/main/java/com/gh/common/databind/BindingAdapters.java index 9cd5aa0f33..bb165a6414 100644 --- a/app/src/main/java/com/gh/common/databind/BindingAdapters.java +++ b/app/src/main/java/com/gh/common/databind/BindingAdapters.java @@ -336,6 +336,13 @@ public class BindingAdapters { } } + @BindingAdapter("gameIcon") + public static void setGameIcon(View view, GameEntity gameEntity) { + if (gameEntity != null && view instanceof GameIconView) { + ((GameIconView) view).displayGameIcon(gameEntity.getIcon(), gameEntity.getIconSubscript()); + } + } + @BindingAdapter("articleType") public static void setArticleType(TextView view, String articleType) { NewsUtils.setNewsType(view, articleType, 0, 0); diff --git a/app/src/main/java/com/gh/common/history/HistoryDatabase.kt b/app/src/main/java/com/gh/common/history/HistoryDatabase.kt index 33e51e8f7f..e6e5683771 100644 --- a/app/src/main/java/com/gh/common/history/HistoryDatabase.kt +++ b/app/src/main/java/com/gh/common/history/HistoryDatabase.kt @@ -26,7 +26,8 @@ import com.halo.assistant.HaloApp ListStringConverter::class, CommunityVideoConverter::class, UserConverter::class, - ImageInfoConverter::class) + ImageInfoConverter::class, + VideoInfoConverter::class) abstract class HistoryDatabase : RoomDatabase() { @@ -79,6 +80,21 @@ abstract class HistoryDatabase : RoomDatabase() { } } + val MIGRATION_8_9: Migration = object : Migration(8, 9) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("Alter TABLE ArticleEntity add des TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE ArticleEntity add url TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE ArticleEntity add videoInfo TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE ArticleEntity add poster TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE ArticleEntity add length INTEGER NOT NULL DEFAULT 0") + database.execSQL("Alter TABLE AnswerEntity add des TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE AnswerEntity add url TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE AnswerEntity add videoInfo TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE AnswerEntity add poster TEXT NOT NULL DEFAULT ''") + database.execSQL("Alter TABLE AnswerEntity add length INTEGER NOT NULL DEFAULT 0") + } + } + val instance by lazy { Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE") .addMigrations(MIGRATION_2_3) @@ -87,6 +103,7 @@ abstract class HistoryDatabase : RoomDatabase() { .addMigrations(MIGRATION_5_6) .addMigrations(MIGRATION_6_7) .addMigrations(MIGRATION_7_8) + .addMigrations(MIGRATION_8_9) .build() } } diff --git a/app/src/main/java/com/gh/common/syncpage/SyncFieldConstants.kt b/app/src/main/java/com/gh/common/syncpage/SyncFieldConstants.kt index 033fd8b95c..672c517793 100644 --- a/app/src/main/java/com/gh/common/syncpage/SyncFieldConstants.kt +++ b/app/src/main/java/com/gh/common/syncpage/SyncFieldConstants.kt @@ -20,4 +20,7 @@ object SyncFieldConstants { // 回答数量 const val ANSWER_COUNT = "ANSWER_COUNT" + // 是否关注 + const val IS_FOLLOWER = "IS_FOLLOWER" + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/BitmapUtils.java b/app/src/main/java/com/gh/common/util/BitmapUtils.java index 99077eeef7..28d834fa37 100644 --- a/app/src/main/java/com/gh/common/util/BitmapUtils.java +++ b/app/src/main/java/com/gh/common/util/BitmapUtils.java @@ -8,6 +8,7 @@ import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.media.ExifInterface; +import android.media.MediaMetadataRetriever; import android.os.Build; import com.halo.assistant.HaloApp; @@ -354,4 +355,25 @@ public class BitmapUtils { return result; } + + // 获取视频缩略图(比较耗时,建议在子线程中调用) + public static Bitmap getVideoThumbnail(String filePath) { + Bitmap bitmap = null; + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + try { + retriever.setDataSource(filePath); + bitmap = retriever.getFrameAtTime(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (RuntimeException e) { + e.printStackTrace(); + } finally { + try { + retriever.release(); + } catch (RuntimeException e) { + e.printStackTrace(); + } + } + return bitmap; + } } diff --git a/app/src/main/java/com/gh/common/util/CommentHelper.kt b/app/src/main/java/com/gh/common/util/CommentHelper.kt index 2aa48589d8..0ef998a1ed 100644 --- a/app/src/main/java/com/gh/common/util/CommentHelper.kt +++ b/app/src/main/java/com/gh/common/util/CommentHelper.kt @@ -74,12 +74,26 @@ object CommentHelper { listener = listener) } + fun showQuestionCommentOption(view: View, + commentEntity: CommentEntity, + questionId: String, + listener: OnCommentOptionClickListener?) { + showCommentOptions(view = view, + commentEntity = commentEntity, + showConversation = false, + questionId = questionId, + isShowTop = true, + ignoreModerator = true, + listener = listener) + } + private fun showCommentOptions(view: View, commentEntity: CommentEntity, showConversation: Boolean, articleId: String? = null, communityId: String? = null, answerId: String? = null, + questionId: String? = null, videoId: String? = null, isShowTop: Boolean = false, ignoreModerator: Boolean = false, @@ -87,7 +101,7 @@ object CommentHelper { listener: OnCommentOptionClickListener? = null) { val context = view.context val dialogOptions = ArrayList() - if (isShowTop && articleId != null && commentEntity.me?.isArticleOrAnswerAuthor == true) { + if (isShowTop && (articleId != null || questionId != null) && commentEntity.me?.isArticleOrAnswerAuthor == true) { dialogOptions.add(if (commentEntity.isTop) "取消置顶" else "置顶") } @@ -97,7 +111,7 @@ object CommentHelper { } if (isVideoAuthor || (videoId != null && commentEntity.user.id == UserManager.getInstance().userId)) { dialogOptions.add("删除评论") - } else if (articleId != null && (commentEntity.user.id == UserManager.getInstance().userId || + } else if ((articleId != null || questionId != null) && (commentEntity.user.id == UserManager.getInstance().userId || commentEntity.me?.isModerator == true || commentEntity.me?.isArticleOrAnswerAuthor == true)) { dialogOptions.add("删除评论") } @@ -156,12 +170,19 @@ object CommentHelper { } } - if (answerId != null) { - PostCommentUtils.postAnswerReportData(context, commentEntity.id, answerId, reportType, commentListener) - } else if (articleId != null) { - PostCommentUtils.reportCommunityArticleComment(context, communityId, articleId, commentEntity.id, reportType, commentListener) - } else { - PostCommentUtils.reportVideoComment(context, videoId, commentEntity.id, reportType, commentListener) + when { + answerId != null -> { + PostCommentUtils.postAnswerReportData(context, commentEntity.id, answerId, reportType, commentListener) + } + articleId != null -> { + PostCommentUtils.reportCommunityArticleComment(context, communityId, articleId, commentEntity.id, reportType, commentListener) + } + questionId != null -> { + PostCommentUtils.reportQuestionComment(context, questionId, commentEntity.id, reportType, commentListener) + } + else -> { + PostCommentUtils.reportVideoComment(context, videoId, commentEntity.id, reportType, commentListener) + } } } } diff --git a/app/src/main/java/com/gh/common/util/CommentUtils.java b/app/src/main/java/com/gh/common/util/CommentUtils.java index 5471cfe04b..57e84b47ac 100644 --- a/app/src/main/java/com/gh/common/util/CommentUtils.java +++ b/app/src/main/java/com/gh/common/util/CommentUtils.java @@ -271,6 +271,7 @@ public class CommentUtils { String articleId, String articleCommunityId, String videoId, + String questionId, final CommentEntity commentEntity, final TextView commentLikeCountTv, final ImageView commentLikeIv, @@ -294,7 +295,7 @@ public class CommentUtils { commentLikeCountTv.setText(NumberUtils.transSimpleCount(commentEntity.getVote())); commentLikeCountTv.setVisibility(View.VISIBLE); - PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, commentEntity.getId(), + PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, questionId, commentEntity.getId(), new PostCommentUtils.PostCommentListener() { @Override public void postSuccess(JSONObject response) { @@ -348,7 +349,7 @@ public class CommentUtils { String entrance = "视频流-评论-点赞"; CheckLoginUtils.checkLogin(context, entrance, () -> { - PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, commentEntity.getId(), + PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, "", commentEntity.getId(), new PostCommentUtils.PostCommentListener() { @Override public void postSuccess(JSONObject response) { diff --git a/app/src/main/java/com/gh/common/util/DialogUtils.java b/app/src/main/java/com/gh/common/util/DialogUtils.java index 35d845943c..411572e341 100644 --- a/app/src/main/java/com/gh/common/util/DialogUtils.java +++ b/app/src/main/java/com/gh/common/util/DialogUtils.java @@ -80,6 +80,7 @@ import com.gh.gamecenter.entity.PrivacyPolicyEntity; import com.gh.gamecenter.entity.SettingsEntity; import com.gh.gamecenter.entity.SimpleGameEntity; import com.gh.gamecenter.entity.TrackableEntity; +import com.gh.gamecenter.setting.GameDownloadSettingFragment; import com.gh.gamecenter.suggest.SuggestType; import com.halo.assistant.HaloApp; import com.halo.assistant.fragment.SettingsFragment; @@ -221,7 +222,7 @@ public class DialogUtils { } else if (NetworkUtils.isWifiConnected(context) || filter4GorSize(context, size)) { callBack.onResponse(false); - } else if (!preferences.getBoolean(SettingsFragment.getTrafficDownloadHintKey(), true)) { + } else if (!preferences.getBoolean(GameDownloadSettingFragment.getTrafficDownloadHintKey(), true)) { AppExecutor.getUiExecutor().executeWithDelay(() -> Utils.toast(context, "当前使用移动网络下载,请注意流量消耗"), 500); callBack.onResponse(false); } else { @@ -296,7 +297,7 @@ public class DialogUtils { PreferenceManager .getDefaultSharedPreferences(finalContext) .edit() - .putBoolean(SettingsFragment.getTrafficDownloadHintKey(), false) + .putBoolean(GameDownloadSettingFragment.getTrafficDownloadHintKey(), false) .apply(); AppExecutor.getUiExecutor().executeWithDelay(() -> { // 显示了弹窗以后,即便下面这个 toast 放在 listener.onConfirm 后调用也是显示 listener.onConfirm 里的 toast diff --git a/app/src/main/java/com/gh/common/util/DirectUtils.kt b/app/src/main/java/com/gh/common/util/DirectUtils.kt index 061dadacd5..ed928a9990 100644 --- a/app/src/main/java/com/gh/common/util/DirectUtils.kt +++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt @@ -50,6 +50,7 @@ import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.column.detail.AskColumnDetailActivity import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity import com.gh.gamecenter.qa.subject.CommunitySubjectActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity import com.gh.gamecenter.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.security.BindPhoneActivity @@ -766,7 +767,7 @@ object DirectUtils { // 这里换个线程操作是为了做一点延时 AppExecutor.ioExecutor.execute { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK)) + EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS)) EventBus.getDefault().post(EBReuse(CommunityFragment.EB_RETRY_PAGE)) } } @@ -781,7 +782,7 @@ object DirectUtils { // 这里换个线程操作是为了做一点延时 AppExecutor.ioExecutor.execute { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK)) + EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS)) EventBus.getDefault().post(EBReuse(CommunityFragment.EB_RETRY_PAGE)) } } @@ -833,7 +834,8 @@ object DirectUtils { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { val bundle = Bundle() bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER) - bundle.putString(KEY_TO, VideoDetailActivity::class.java.name) + bundle.putString(KEY_TO, ForumVideoDetailActivity::class.java.name) + bundle.putString(KEY_VIDEO_ID, videoId); bundle.putString(KEY_PATH, path) bundle.putString(KEY_ID, videoId) bundle.putString(KEY_GAMEID, gameId) @@ -1177,10 +1179,10 @@ object DirectUtils { // 这里换个线程操作是为了做一点延时 AppExecutor.ioExecutor.execute { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK)) + EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS)) } } else { - jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_ASK) }) + jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_BBS) }) } } @@ -1461,4 +1463,19 @@ object DirectUtils { url = String.format(Locale.CHINA, "%s×tamp=%d", url, (Date().time / 1000 / 1000.toFloat()).roundToInt()) directToFullScreenWebPage(context, url, true) } + + /** + * 跳转至活动详情 + */ + @JvmStatic + fun directToActivityDetail(context: Context, activityId: String, entrance: String) { + var url: String = if (isPublishEnv()) { + Constants.ACTIVITY_DETAIL_ADDRESS + } else { + Constants.ACTIVITY_DETAIL_ADDRESS + } + + url = String.format(Locale.CHINA, "%s&id=%s×tamp=%d", url, activityId, (Date().time / 1000 / 1000.toFloat()).roundToInt()) + directToWebView(context, url, entrance) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/EntranceUtils.java b/app/src/main/java/com/gh/common/util/EntranceUtils.java index 982e02238d..04a853c131 100644 --- a/app/src/main/java/com/gh/common/util/EntranceUtils.java +++ b/app/src/main/java/com/gh/common/util/EntranceUtils.java @@ -236,6 +236,11 @@ public class EntranceUtils { public static final String KEY_IS_QA_FEEDBACK = "is_qa_feedback"; public static final String KEY_IS_CLICK_RECEIVE_BTN = "is_click_receive_btn"; public static final String KEY_SHOW_QUICK_LOGIN = "show_quick_login"; + public static final String KEY_VIDEO_LIST = "video_list"; + public static final String KEY_CHOOSE_FORUM_TYPE = "choose_forum_type"; + public static final String KEY_CHOOSE_MAX_COUNT = "choose_max_count"; + public static final String KEY_COMMENT_COUNT = "comment_count"; + public static final String KEY_IS_COMMENT_CONVERSATION = "is_comment_conversation"; public static void jumpActivity(Context context, Bundle bundle) { bundle.putBoolean(KEY_REQUIRE_REDIRECT, true); diff --git a/app/src/main/java/com/gh/common/util/ImageUtils.kt b/app/src/main/java/com/gh/common/util/ImageUtils.kt index 356fd00b82..29d2e03e5f 100644 --- a/app/src/main/java/com/gh/common/util/ImageUtils.kt +++ b/app/src/main/java/com/gh/common/util/ImageUtils.kt @@ -19,6 +19,7 @@ import com.facebook.drawee.controller.ControllerListener import com.facebook.drawee.drawable.ScalingUtils import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder import com.facebook.drawee.view.SimpleDraweeView +import com.facebook.imagepipeline.common.ResizeOptions import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber import com.facebook.imagepipeline.image.CloseableImage import com.facebook.imagepipeline.image.ImageInfo @@ -90,7 +91,7 @@ object ImageUtils { val jpegConfig = Config.getSettings()?.image?.oss?.jpeg val webpConfig = Config.getSettings()?.image?.oss?.webp ?: Config.getSettings()?.image?.oss?.gif - if (jpegConfig != null) { + if (imageUrl?.contains("?x-oss-process") == false && jpegConfig != null) { return if (width == 0 || width == null) { "$imageUrl$webpConfig" } else { @@ -452,6 +453,19 @@ object ImageUtils { draweeView.setImageURI("res:///" + res) } + @JvmStatic + fun displayResizeMedia(draweeView: SimpleDraweeView, url: String, resizeWidthDp: Int, resizeHeightDp: Int) { + val request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) + .setResizeOptions(ResizeOptions(resizeWidthDp, resizeHeightDp)) + .build() + val controller = Fresco.newDraweeControllerBuilder() + .setImageRequest(request) + .setOldController(draweeView.controller) + .setControllerListener(BaseControllerListener()) + .build() + draweeView.controller = controller + } + //预加载图片 @JvmStatic fun prefetchToDiskCache(url: String) { diff --git a/app/src/main/java/com/gh/common/util/NewLogUtils.kt b/app/src/main/java/com/gh/common/util/NewLogUtils.kt index 2338bf2d5a..7870f789e9 100644 --- a/app/src/main/java/com/gh/common/util/NewLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewLogUtils.kt @@ -3,7 +3,10 @@ package com.gh.common.util import com.gh.common.json.json import com.gh.common.loghub.LoghubUtils import com.gh.gamecenter.entity.LinkEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp import com.lightgame.utils.Utils +import io.reactivex.schedulers.Schedulers import org.json.JSONObject object NewLogUtils { @@ -25,6 +28,19 @@ object NewLogUtils { log(json, "event", false) } + fun logForumContentBrowser(contentId: String, contentType: String) { + val requestBody = hashMapOf( + Pair("content_id", contentId), + Pair("content_type", contentType) + ).createRequestBody() + + RetrofitManager.getInstance(HaloApp.getInstance()) + .api + .postBrowses(requestBody) + .subscribeOn(Schedulers.io()) + .subscribe() + } + private fun log(jsonObject: JSONObject, logStore: String, uploadImmediately: Boolean) { Utils.log("NewLogUtils", jsonObject.toString(4)) LoghubUtils.log(jsonObject, logStore, uploadImmediately) diff --git a/app/src/main/java/com/gh/common/util/PostCommentUtils.java b/app/src/main/java/com/gh/common/util/PostCommentUtils.java index 63e6a96544..dec59ec097 100644 --- a/app/src/main/java/com/gh/common/util/PostCommentUtils.java +++ b/app/src/main/java/com/gh/common/util/PostCommentUtils.java @@ -117,6 +117,7 @@ public class PostCommentUtils { String articleId, String articleCommunityId, String videoId, + String questionId, final String commentId, final PostCommentListener listener) { @@ -126,6 +127,8 @@ public class PostCommentUtils { observable = RetrofitManager.getInstance(context).getApi().postVoteAnswerComment(answerId, commentId); } else if (!TextUtils.isEmpty(articleId)) { observable = RetrofitManager.getInstance(context).getApi().postVoteCommunityArticleComment(articleCommunityId, articleId, commentId); + } else if (!TextUtils.isEmpty(questionId)) { + observable = RetrofitManager.getInstance(context).getApi().postVoteQuestionComment(questionId, commentId); } else { observable = RetrofitManager.getInstance(context).getApi().postVoteToVideo(videoId, commentId); } @@ -258,6 +261,28 @@ public class PostCommentUtils { } }); } + public static void reportQuestionComment(final Context context, + final String questionId, + final String commentId, + final String reportData, + final PostCommentListener listener) { + RequestBody body = RequestBody.create(MediaType.parse("application/json"), reportData); + RetrofitManager.getInstance(context).getApi() + .postQuestionCommentReport(questionId, commentId, body) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Response() { + @Override + public void onResponse(ResponseBody response) { + listener.postSuccess(null); + } + + @Override + public void onFailure(HttpException e) { + listener.postFailed(e); + } + }); + } public interface PostCommentListener { void postSuccess(JSONObject response); diff --git a/app/src/main/java/com/gh/common/util/ShareUtils.java b/app/src/main/java/com/gh/common/util/ShareUtils.java index af3e522e63..667eb403e4 100644 --- a/app/src/main/java/com/gh/common/util/ShareUtils.java +++ b/app/src/main/java/com/gh/common/util/ShareUtils.java @@ -409,6 +409,11 @@ public class ShareUtils { //QQ分享 public void qqShare() { Utils.toast(mContext, R.string.share_skip); + + mShareType = ShareType.qq; + shareType = "qq_friend"; + LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + Bundle params = new Bundle(); switch (mShareEntrance) { @@ -445,6 +450,10 @@ public class ShareUtils { public void wechatShare() { Utils.toast(mContext, R.string.share_skip); + shareType = "wechat_friend"; + mShareType = ShareType.wechat; + LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) { Utils.toast(mContext, "没安装微信,分享失败"); return; @@ -561,6 +570,12 @@ public class ShareUtils { //QQ空间分享 public void qZoneShare() { Utils.toast(mContext, R.string.share_skip); + + mShareType = ShareType.qqZone; + shareType = "qq_zone"; + MtaHelper.onEvent("内容分享", "QQ空间", mTitle); + LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + Bundle params = new Bundle(); switch (mShareEntrance) { @@ -601,6 +616,10 @@ public class ShareUtils { public void wechatMomentsShare() { Utils.toast(mContext, R.string.share_skip); + mShareType = ShareType.wechatMoments; + shareType = "wechat_moment"; + LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) { Utils.toast(mContext, "没安装微信,分享失败"); return; @@ -642,6 +661,10 @@ public class ShareUtils { //新浪微博分享 public void sinaWeiboShare() { + mShareType = ShareType.weibo; + shareType = "sina_weibo"; + LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + WbSdk.install(mContext, new AuthInfo(mContext, Config.WEIBO_APPKEY, "http://www.sina.com", WEIBO_SCOPE)); if (mShareEntrance == ShareEntrance.qaDetail) { @@ -668,6 +691,10 @@ public class ShareUtils { //短信分享 public void shortMessageShare() { + mShareType = ShareType.weibo; + shareType = "sms"; + LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + String smsBody; switch (mShareEntrance) { case news: @@ -714,6 +741,7 @@ public class ShareUtils { public void copyLink(String copyContent) { shareType = "copy_link"; LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + LogUtils.uploadShareResult(shareType, shareEntrance.getName(), "success", shareUrl, mTitle, mSummary, resourceId); if (mShareEntrance != ShareEntrance.shareGh) { ExtensionsKt.copyTextAndToast(copyContent, "复制成功"); safelyDismiss(); @@ -749,50 +777,34 @@ public class ShareUtils { holder.shareLogo.setImageResource(arrLogo[position]); holder.shareLabel.setText(arrLabel[position]); holder.itemView.setOnClickListener(v -> { - if (mShareEntrance == ShareEntrance.shareGh) { - MtaHelper.onEvent("我的光环_新", "分享光环", arrLabel[position]); - } if (listener != null) { listener.onItemClick(holder.getAdapterPosition()); } switch (holder.getPosition()) { case 0: - shareType = "wechat_friend"; - MtaHelper.onEvent("内容分享", "微信好友", mTitle); - LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); wechatShare(); break; case 1: - shareType = "wechat_moment"; - MtaHelper.onEvent("内容分享", "微信朋友圈", mTitle); - LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); wechatMomentsShare(); break; case 2: - shareType = "qq_friend"; - MtaHelper.onEvent("内容分享", "QQ好友", mTitle); - LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); qqShare(); break; case 3: - shareType = "qq_zone"; - MtaHelper.onEvent("内容分享", "QQ空间", mTitle); - LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); qZoneShare(); break; case 4: - shareType = "sina_weibo"; - MtaHelper.onEvent("内容分享", "新浪微博", mTitle); - LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); sinaWeiboShare(); break; case 5: - MtaHelper.onEvent("内容分享", "短信", mTitle); shortMessageShare(); break; case 6: - MtaHelper.onEvent("内容分享", "复制链接", mTitle); + shareType = "copy_link"; + LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + LogUtils.uploadShareResult(shareType, shareEntrance.getName(), "success", shareUrl, mTitle, mSummary, resourceId); + if (mShareEntrance == ShareEntrance.askInvite) { copyLink(mTitle + " - 光环助手" + shareUrl); } else if (mShareEntrance == ShareEntrance.askNormal || mShareEntrance == ShareEntrance.answerNormal) { @@ -815,6 +827,7 @@ public class ShareUtils { } else { shareType = "copy_link"; LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId); + LogUtils.uploadShareResult(shareType, shareEntrance.getName(), "success", shareUrl, mTitle, mSummary, resourceId); } break; } diff --git a/app/src/main/java/com/gh/common/view/CustomDividerItemDecoration.kt b/app/src/main/java/com/gh/common/view/CustomDividerItemDecoration.kt index 8e7e30d323..060fd1d0b6 100644 --- a/app/src/main/java/com/gh/common/view/CustomDividerItemDecoration.kt +++ b/app/src/main/java/com/gh/common/view/CustomDividerItemDecoration.kt @@ -1,11 +1,13 @@ package com.gh.common.view import android.content.Context +import android.graphics.Canvas import android.graphics.Rect import android.view.View import android.widget.LinearLayout import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView +import kotlin.math.roundToInt class CustomDividerItemDecoration( context: Context, @@ -13,6 +15,7 @@ class CustomDividerItemDecoration( var notDecorateTheFirstItem: Boolean = false, var notDecorateTheLastItem: Boolean = false, var notDecorateTheFirstTwoItems: Boolean = false) : DividerItemDecoration(context, LinearLayout.VERTICAL) { + private val mBounds = Rect() override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { if (onlyDecorateTheFirstItem) { @@ -31,4 +34,42 @@ class CustomDividerItemDecoration( } } + override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + if (parent.layoutManager == null || drawable == null) { + return + } + drawVertical(c, parent) + } + + private fun drawVertical(canvas: Canvas, parent: RecyclerView) { + canvas.save() + val left: Int + val right: Int + if (parent.clipToPadding) { + left = parent.paddingLeft + right = parent.width - parent.paddingRight + canvas.clipRect(left, parent.paddingTop, right, + parent.height - parent.paddingBottom) + } else { + left = 0 + right = parent.width + } + val childCount = parent.childCount + for (i in 0 until childCount) { + val child = parent.getChildAt(i) + parent.getDecoratedBoundsWithMargins(child, mBounds) + var bottom = mBounds.bottom + child.translationY.roundToInt() + val rect = Rect() + getItemOffsets(rect, child, parent, RecyclerView.State()) + val top = if (rect.bottom > 0) { + bottom - (drawable?.intrinsicHeight ?: 0) + } else { + bottom = 0 + 0 + } + drawable?.setBounds(left, top, right, bottom) + drawable?.draw(canvas) + } + canvas.restore() + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/view/FlexLinearLayout.kt b/app/src/main/java/com/gh/common/view/FlexLinearLayout.kt index 80d210e70c..a4a96b9e4c 100644 --- a/app/src/main/java/com/gh/common/view/FlexLinearLayout.kt +++ b/app/src/main/java/com/gh/common/view/FlexLinearLayout.kt @@ -12,6 +12,8 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.core.content.ContextCompat import com.gh.common.util.DisplayUtils +import com.gh.common.util.dip2px +import com.gh.common.util.sp2px import com.gh.gamecenter.R import com.gh.gamecenter.entity.TagStyleEntity import kotlin.math.ceil @@ -23,16 +25,27 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib private var mTotalCount = 0 private var mTags = ArrayList() - private var mItemHeight = DisplayUtils.dip2px(20F) - private var mPadding = DisplayUtils.dip2px(5F) - private var mMargin = DisplayUtils.dip2px(4F) + private var mItemHeight = 0 + private var mPadding = 0 + private var mMargin = 0 private var mTextSize = 10F - private var mLastItemWidth = DisplayUtils.dip2px(18F)//最后更多按钮宽度 + private var mLastItemWidth = 0//最后更多按钮宽度 private var mTotalWidth = 0 + private var mStrokeWidth = 0 var onClickListener: OnItemClickListener? = null init { gravity = Gravity.CENTER_VERTICAL + + val ta = context.obtainStyledAttributes(attrs, R.styleable.FlexLinearLayout) + mItemHeight = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemHeight, 20f.dip2px()) + mPadding = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemPadding, 5f.dip2px()) + mMargin = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemMargin, 4f.dip2px()) + mTextSize = ta.getDimension(R.styleable.FlexLinearLayout_itemTextSize, 10f.sp2px().toFloat()) + mLastItemWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_lastItemWidth, 18f.dip2px()) + mStrokeWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_strokeWidth, 1f.dip2px()) + + ta.recycle() } fun setTags(tags: ArrayList) { @@ -40,7 +53,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib mTotalCount = tags.size mTotalWidth = measuredWidth val paint = Paint() - paint.textSize = DisplayUtils.sp2px(context, mTextSize).toFloat() + paint.textSize = mTextSize var currentWidth = mLastItemWidth.toFloat() tags.forEach { @@ -79,7 +92,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib layoutParams = params setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_game_detail_label_more)) background = createBackgroundDrawable() - scaleType=ImageView.ScaleType.CENTER + scaleType = ImageView.ScaleType.CENTER } imageView.setOnClickListener { onClickListener?.onMoreClickListener() @@ -92,7 +105,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib return TextView(context).apply { text = tag.name includeFontPadding = false - textSize = mTextSize + textSize = DisplayUtils.px2sp(context, mTextSize).toFloat() gravity = Gravity.CENTER setTextColor(Color.parseColor("#333333")) @@ -112,7 +125,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib private fun createBackgroundDrawable(): GradientDrawable { val gradientDrawable = GradientDrawable() gradientDrawable.setColor(Color.TRANSPARENT) - gradientDrawable.setStroke(DisplayUtils.dip2px(context, 1f), Color.parseColor("#C2C6CC")) + gradientDrawable.setStroke(mStrokeWidth, Color.parseColor("#C2C6CC")) gradientDrawable.cornerRadius = DisplayUtils.dip2px(2f).toFloat() return gradientDrawable } diff --git a/app/src/main/java/com/gh/common/view/ImageContainerView.kt b/app/src/main/java/com/gh/common/view/ImageContainerView.kt index b464df5bd3..77133eba0f 100644 --- a/app/src/main/java/com/gh/common/view/ImageContainerView.kt +++ b/app/src/main/java/com/gh/common/view/ImageContainerView.kt @@ -72,7 +72,7 @@ class ImageContainerView : LinearLayout { mPath = path index = 0 removeAllViews() - if (entity.getPassVideos().isNullOrEmpty() && entity.images.isNullOrEmpty()) { + if (entity.getPassVideos().isNotEmpty() || entity.images.isNullOrEmpty()) { visibility = View.GONE return } diff --git a/app/src/main/java/com/gh/common/view/RichEditor.java b/app/src/main/java/com/gh/common/view/RichEditor.java index eeacb3755b..0a80e4b6c7 100644 --- a/app/src/main/java/com/gh/common/view/RichEditor.java +++ b/app/src/main/java/com/gh/common/view/RichEditor.java @@ -11,6 +11,7 @@ import android.util.AttributeSet; import android.view.Gravity; import android.webkit.JavascriptInterface; import android.webkit.WebChromeClient; +import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -463,6 +464,49 @@ public class RichEditor extends WebView { exec("javascript:RE.replacePlaceholderImage('" + list + "');"); } + /** + * 插入视频占位图 + */ + public void insertPlaceholderVideo(String id, String poster) { + exec("javascript:RE.insertPlaceholderVideo('" + id + "','" + poster + "');"); + } + + /** + * 更新视频进度条 + */ + public void updateVideoProgress(String id, String progress) { + exec("javascript:RE.updateVideoProgress('" + id + "','" + progress + "');"); + } + + + /** + * 上传视频完成 + */ + public void videoUploadFinished(String id, String url, String msg) { + exec("javascript:RE.videoUploadFinished('" + id + "','" + url + "','" + msg + "');"); + } + + /** + * 更换封面图 + */ + public void changePoster(String id, String poster) { + exec("javascript:RE.changePoster('" + id + "','" + poster + "');"); + } + + /** + * 删除视频 + */ + public void delPlaceholderVideo(String id) { + exec("javascript:RE.delPlaceholderVideo('" + id + "');"); + } + + /** + * 上传失败 + */ + public void videoUploadFailed(String id) { + exec("javascript:RE.videoUploadFailed('" + id + "');"); + } + public void removeFormat() { exec("javascript:RE.removeFormat();"); } diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index ca67c05302..ff2b3e7641 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -307,8 +307,6 @@ public class MainActivity extends BaseActivity { } }); - //恢复顶部视频默认静音状态 - SPUtils.setBoolean(Constants.SP_TOP_VIDEO_VOICE, true); //恢复视频流非Wifi提醒 SPUtils.setBoolean(Constants.SP_NON_WIFI_TIPS, true); //重置首页视频播放进度 @@ -749,7 +747,7 @@ public class MainActivity extends BaseActivity { private void switchToCommunityTabAndRefresh() { getIntent().putExtra(SWITCH_TO_COMMUNITY, false); Log.e("Switch", "true"); - EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK)); + EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS)); EventBus.getDefault().post(new EBReuse(CommunityFragment.EB_RETRY_PAGE)); } @@ -882,7 +880,7 @@ public class MainActivity extends BaseActivity { if (info != null) { if (EntranceUtils.HOST_COMMUNITY.equals(info.getType())) { UserManager.getInstance().setCommunityData(new CommunityEntity(info.getLink(), info.getText())); - runOnUiThread(() -> mMainWrapperFragment.setCurrentItem(MainWrapperFragment.INDEX_ASK)); + runOnUiThread(() -> mMainWrapperFragment.setCurrentItem(MainWrapperFragment.INDEX_BBS)); } else { DirectUtils.directToSpecificPage(this, info.getType(), diff --git a/app/src/main/java/com/gh/gamecenter/baselist/ListFragment.java b/app/src/main/java/com/gh/gamecenter/baselist/ListFragment.java index 60a75ca6e5..8bb30e0383 100644 --- a/app/src/main/java/com/gh/gamecenter/baselist/ListFragment.java +++ b/app/src/main/java/com/gh/gamecenter/baselist/ListFragment.java @@ -145,6 +145,7 @@ public abstract class ListFragment extends Ba loadStatusControl(response.size()); } + public MutableLiveData> getListLiveData(){ + return mListLiveData; + } + private void handleFailure(Exception exception) { if (exception instanceof HttpException && ((HttpException) exception).code() == 404) { loadStatusControl(0); diff --git a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt index 4fbd87807d..aa410163f5 100644 --- a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt @@ -8,10 +8,7 @@ import butterknife.BindView import com.ethanhua.skeleton.Skeleton import com.gh.common.constant.Constants import com.gh.common.exposure.ExposureListener -import com.gh.common.util.DialogUtils -import com.gh.common.util.EntranceUtils -import com.gh.common.util.observeNonNull -import com.gh.common.util.viewModelProvider +import com.gh.common.util.* import com.gh.common.view.ConfigFilterView import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus @@ -139,7 +136,7 @@ class NewCategoryListFragment : ListFragment implements ISyncAda holder.itemView.setOnClickListener(v -> { if (entity.getActive()) { - mContext.startActivity(AnswerDetailActivity.getIntent(mContext, entity.getId(), mEntrance, path)); +// mContext.startActivity(AnswerDetailActivity.getIntent(mContext, entity.getId(), mEntrance, path)); + mContext.startActivity(NewQuestionDetailActivity.getCommentIntent(mContext, entity.getQuestions().getId(), entity.getId(), mEntrance, path)); } else { showDeleteDialog(entity.getId()); } @@ -112,7 +115,8 @@ public class AnswerAdapter extends ListAdapter implements ISyncAda }); viewHolder.getBinding().title.setOnClickListener(v -> { Questions questions = entity.getQuestions(); - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.getId(), mEntrance, path)); +// mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.getId(), mEntrance, path)); + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, questions.getId(), mEntrance, path)); }); break; case ItemViewType.ITEM_FOOTER: diff --git a/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleAdapter.kt b/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleAdapter.kt index cdd6e931dc..abf9d03cb2 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleAdapter.kt @@ -13,9 +13,11 @@ import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.CommunityAnswerItemBinding +import com.gh.gamecenter.entity.CommunityEntity import com.gh.gamecenter.qa.answer.CommunityAnswerItemViewHolder import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.ArticleEntity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity class CommunityArticleAdapter(context: Context, private val mViewModel: CommunityArticleViewModel, @@ -68,14 +70,21 @@ class CommunityArticleAdapter(context: Context, } } + if (entity.bbs == CommunityEntity()) { + entity.bbs = entity.community + } holder.bindArticleItem(entity, mEntrance, path) holder.itemView.setOnClickListener { - if (entity.active) { - mContext.startActivity(ArticleDetailActivity.getIntent(mContext, entity.community, entity.id, mEntrance, path)) + if (entity.type == "question") { + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, entity.id, mEntrance, path)) } else { - DialogUtils.showCancelAlertDialog(mContext, "提示", "内容已被删除,是否取消收藏?", "取消收藏", "暂不", { - mViewModel.deleteCollection(entity.community.id, entity.id) - }, null) + if (entity.active) { + mContext.startActivity(ArticleDetailActivity.getIntent(mContext, entity.community, entity.id, mEntrance, path)) + } else { + DialogUtils.showCancelAlertDialog(mContext, "提示", "内容已被删除,是否取消收藏?", "取消收藏", "暂不", { + mViewModel.deleteCollection(entity.community.id, entity.id) + }, null) + } } if (!entity.read) { diff --git a/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt b/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt index 28e125a946..41d3df7963 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt @@ -9,6 +9,7 @@ import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListViewModel import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.ArticleEntity import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.Response @@ -17,11 +18,11 @@ import com.lightgame.utils.Utils import io.reactivex.Observable import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.Function import io.reactivex.schedulers.Schedulers import okhttp3.ResponseBody import retrofit2.HttpException - class CommunityArticleViewModel(application: Application) : ListViewModel(application) { var type: String = CommunityArticleFragment.Type.COLLECTION.value @@ -32,7 +33,17 @@ class CommunityArticleViewModel(application: Application) : ListViewModel> { return if (type == CommunityArticleFragment.Type.COLLECTION.value) { - Single.fromObservable(RetrofitManager.getInstance(getApplication()).api.getCollectionCommunityArticle(UserManager.getInstance().userId, page)) + Single.fromObservable( + RetrofitManager.getInstance(getApplication()).api + .getCollectionArticleAndQuestion(UserManager.getInstance().userId, page) + .flatMap(Function, Observable>> { list -> + Observable.create { emitter-> + val articleList = list.map { it.transformArticleEntity() }.toList() + emitter.onNext(articleList) + emitter.onComplete() + } + }) + ) } else { if (page > 5) { Single.create { it.onSuccess(arrayListOf()) } diff --git a/app/src/main/java/com/gh/gamecenter/energy/TaskFragment.kt b/app/src/main/java/com/gh/gamecenter/energy/TaskFragment.kt index 4bf5c9e315..f47ebd9e65 100644 --- a/app/src/main/java/com/gh/gamecenter/energy/TaskFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/energy/TaskFragment.kt @@ -10,7 +10,7 @@ class TaskFragment: ListFragment() { private var mAdapter: TaskAdapter? = null - override fun provideListAdapter()= mAdapter + override fun provideListAdapter() = mAdapter ?: TaskAdapter(requireContext()).apply { mAdapter = this } override fun provideListViewModel(): TaskViewModel = viewModelProvider() diff --git a/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt index 96024b077b..92f57b65af 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt @@ -7,30 +7,33 @@ import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.Parcelize @Parcelize -data class CommentEntity(@SerializedName("_id") - var id: String? = null, - var user: UserEntity = UserEntity(), - var parent: ArticleCommentParent? = null, - @SerializedName("parent_user") - var parentUser: CommentParentEntity? = null, - var content: String? = null, - @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_VOTE_COUNT]) - var vote: Int = 0, - @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_REPLY_COUNT]) - var reply: Int = 0, - var time: Long = 0, - var priority: Int = 0, - @SerializedName("me") - var me: MeEntity? = null, - @SerializedName("is_top") - var isTop: Boolean = false,//是否置顶 - // 楼数,本地字段 - var floor: Int = 0, - @SerializedName("attached") // 楼中楼 - var subCommentList: ArrayList? = null) : Parcelable { +data class CommentEntity( + @SerializedName("_id") + var id: String? = null, + var user: UserEntity = UserEntity(), + var parent: ArticleCommentParent? = null, + @SerializedName("parent_user") + var parentUser: CommentParentEntity? = null, + var content: String? = null, + @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_VOTE_COUNT]) + var vote: Int = 0, + @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_REPLY_COUNT]) + var reply: Int = 0, + var time: Long = 0, + var priority: Int = 0, + @SerializedName("me") + var me: MeEntity? = null, + @SerializedName("is_top") + var isTop: Boolean = false,//是否置顶 + var type: String = "comment",//comment/answer,区分问题评论旧数据,answer表示旧数据(回答) + + // 楼数,本地字段 + var floor: Int = 0, + @SerializedName("attached") // 楼中楼 + var subCommentList: ArrayList? = null) : Parcelable { fun clone(): CommentEntity { - return CommentEntity(id, user, parent, parentUser, content, vote, reply, time, priority, me, isTop, floor, subCommentList) + return CommentEntity(id, user, parent, parentUser, content, vote, reply, time, priority, me, isTop, type, floor, subCommentList) } companion object { diff --git a/app/src/main/java/com/gh/gamecenter/entity/CommunityEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/CommunityEntity.kt index 7158d242d9..8bde53e686 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/CommunityEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/CommunityEntity.kt @@ -14,6 +14,7 @@ data class CommunityEntity( var icon: String? = "", @SerializedName("icon_subscript") var iconSubscript: String? = null, + var type: String = "game_bbs",//game_bbs/official_bbs var game: SimpleGame? = null) : Parcelable { constructor(id: String = "", name: String = "") : this(id, name, "choiceness") diff --git a/app/src/main/java/com/gh/gamecenter/entity/ForumActivityEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ForumActivityEntity.kt new file mode 100644 index 0000000000..56f361d081 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/ForumActivityEntity.kt @@ -0,0 +1,40 @@ +package com.gh.gamecenter.entity + +import com.google.gson.annotations.SerializedName + +data class ForumActivityCategoryEntity( + @SerializedName("_id") + var id: String = "", + var name: String = "" +) + +data class ForumActivityEntity( + @SerializedName("_id") + val id: String = "", + val title: String = "", + @SerializedName("tag_activity_id") + val activityId: String = "", //活动标签ID + @SerializedName("tag_activity_name") + val activityName: String = "", //活动标签名 + @SerializedName("game_id") + val gameId: String = "", + @SerializedName("game_name") + val gameName: String = "", + @SerializedName("effect_time") + val effectTime: ActivityTime = ActivityTime(), + @SerializedName("award_time") + val awardTime: ActivityTime = ActivityTime(), + val image: ActivityImage = ActivityImage(), + val name: String = "", + val unread: Boolean = false +) { + + data class ActivityTime( + val start: Long = 0, + val end: Long = 0 + ) + + data class ActivityImage( + val cover: String = "" + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/ForumDetailEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ForumDetailEntity.kt index 6e8e72c3be..d94d423282 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/ForumDetailEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/ForumDetailEntity.kt @@ -9,10 +9,24 @@ data class ForumDetailEntity( var game: SimpleGame = SimpleGame(), var background: String = "", var name: String = "", + var icon: String = "", var moderator: ArrayList = arrayListOf(), @SerializedName("top") var topLink: ArrayList = arrayListOf(), @SerializedName("zone_tab") var zone: ZoneEntity? = null, - var me: MeEntity = MeEntity() -) \ No newline at end of file + var me: MeEntity = MeEntity(), + var type: String = "" //game_bbs/official_bbs +) { + fun convertForumDetailEntityToForumEntity(): ForumEntity { + val forumEntity = ForumEntity() + forumEntity.id = id + forumEntity.name = name + forumEntity.icon = icon + forumEntity.game = game + forumEntity.orderTag = System.currentTimeMillis() + forumEntity.unread = false + forumEntity.type = type + return forumEntity + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/ForumEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ForumEntity.kt index 2761bfc5f5..bd408090f5 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/ForumEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/ForumEntity.kt @@ -1,17 +1,33 @@ package com.gh.gamecenter.entity +import androidx.room.Entity +import androidx.room.PrimaryKey import com.google.gson.annotations.SerializedName +@Entity data class ForumEntity( + @PrimaryKey @SerializedName("_id") var id: String = "", var game: SimpleGame = SimpleGame(), var name: String = "", + var icon: String = "", @SerializedName("is_follow") var isFollow: Boolean = false, //本地字段,判断是否是推荐论坛 - var isRecommend: Boolean = false -) + var isRecommend: Boolean = false, + var orderTag: Long = 0, + var unread: Boolean = false, + var type: String = "" //game_bbs/official_bbs +) { + fun transformUnreadEntity(): ForumUnreadEntity { + val forumUnreadEntity = ForumUnreadEntity() + forumUnreadEntity.id = id + forumUnreadEntity.name = name + forumUnreadEntity.unread = unread + return forumUnreadEntity + } +} data class ForumCategoryEntity( @SerializedName("_id") diff --git a/app/src/main/java/com/gh/gamecenter/entity/ForumUnreadEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ForumUnreadEntity.kt new file mode 100644 index 0000000000..03eee93be7 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/ForumUnreadEntity.kt @@ -0,0 +1,13 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.android.parcel.Parcelize + +@Parcelize +data class ForumUnreadEntity( + @SerializedName("_id") + var id: String = "", + var name: String = "", + var unread: Boolean = false +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/ForumVideoEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ForumVideoEntity.kt new file mode 100644 index 0000000000..dd4bb6a4c5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/ForumVideoEntity.kt @@ -0,0 +1,50 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import com.gh.gamecenter.qa.entity.Count +import com.google.gson.annotations.SerializedName +import kotlinx.android.parcel.IgnoredOnParcel +import kotlinx.android.parcel.Parcelize + +@Parcelize +open class ForumVideoEntity( + @SerializedName("_id") + var id: String = "", + var title: String = "", + var des: String = "", + var poster: String = "", + var url: String = "", + val user: PersonalEntity = PersonalEntity(), + val me: MeEntity = MeEntity(), + val size: Long = 0, + var length: Long = 0, + // 有三种状态 pass通过,fail未通过,pending审核中 + var status: String = "", + val format: String = "Mp4", + var game: GameEntity? = null, + @SerializedName("tag_activity_id") + var tagActivityId: String = "",//活动标签的id + @SerializedName("tag_activity_name") + var tagActivityName: String = "", + @SerializedName("bbs_id") + var bbsId: String = "", + @SerializedName("game_id") + var gameId: String = "", + var type: String = "", + var share: Int = 0, + var time: Time = Time(), + @SerializedName("video_info") + var videoInfo: VideoInfo = VideoInfo(), + @SerializedName("is_jx") + var isHighlighted: Boolean = true, + var bbs: CommunityEntity? = null, + var count: Count = Count(), + @IgnoredOnParcel + var videoIsMuted: Boolean = false //是否静音标记 +) : Parcelable + +@Parcelize +data class Time(val upload: Long = 0, val audit: Long = 0, val update: Long = 0) : Parcelable + +@Parcelize +data class VideoInfo(var width: Int = 0, var height: Int = 0) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/LocalVideoEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/LocalVideoEntity.kt new file mode 100644 index 0000000000..d02178a2dc --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/LocalVideoEntity.kt @@ -0,0 +1,16 @@ +package com.gh.gamecenter.entity + +import android.net.Uri +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +data class LocalVideoEntity( + var id: String = "", + var filePath: String = "", + var poster: String = "", + var contentUri: Uri? = null, + var duration: Long = 0, + var format: String = "", + var size: Long = 0 +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt index 78c673a3b8..9726c7979b 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/MeEntity.kt @@ -52,19 +52,23 @@ class MeEntity(@SerializedName("is_community_voted") @SerializedName("is_article_favorite") var isArticleFavorite: Boolean = false, + @SerializedName("is_question_favorite") + var isQuestionFavorite: Boolean = false, + @SerializedName("is_toolkit_favorite") var isToolkitFavorite: Boolean = false, - @SerializedName("is_comment_own", alternate = ["is_answer_commented", "is_community_article_commented", "is_video_commented"]) + @SerializedName("is_comment_own", alternate = ["is_answer_commented", "is_community_article_commented", "is_video_commented", "is_question_commented"]) var isCommentOwner: Boolean = false, // 是否是当前评论的拥有者 @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_VOTE]) - @SerializedName("is_comment_voted", alternate = ["is_answer_comment_voted", "is_video_comment_voted", "is_community_article_comment_voted"]) + @SerializedName("is_comment_voted", alternate = ["is_answer_comment_voted", "is_video_comment_voted", "is_community_article_comment_voted", "is_question_comment_voted"]) var isCommentVoted: Boolean = false, // 是否已经点赞过当前评论 @SerializedName("is_version_requested") var isVersionRequested: Boolean = false, + @SyncPage(syncNames = [SyncFieldConstants.IS_FOLLOWER]) @SerializedName("is_follower", alternate = ["is_follow"]) var isFollower: Boolean = false, // 是否已经关注该用户 @@ -102,8 +106,8 @@ class MeEntity(@SerializedName("is_community_voted") @SerializedName("permissions") val moderatorPermissions: Permissions = Permissions(), - @SerializedName("is_answer_author", alternate = ["is_community_article_author"]) - val isArticleOrAnswerAuthor: Boolean = false, //用于判断是否是当前评论关联的回答或文章的作者 + @SerializedName("is_answer_author", alternate = ["is_community_article_author", "is_question_author"]) + val isArticleOrAnswerAuthor: Boolean = false, //用于判断是否是当前评论关联的回答/文章/问题的作者 @SerializedName("article_draft") var articleDraft: ArticleDraftEntity? = null,//帖子详情可能返回草稿 @@ -159,8 +163,11 @@ data class Permissions( var topCommunityArticleComment: Int = GUEST, // 置顶文章评论 @SerializedName("hide-community-article-comment") - var hideCommunityArticleComment: Int = GUEST // 隐藏文章评论 + var hideCommunityArticleComment: Int = GUEST, // 隐藏文章评论 + //视频相关 + @SerializedName("choiceness-video") + var highlightVideo: Int = GUEST // 加精视频帖 ) : Parcelable { companion object { // -1 为无权限,0 为初级权限,1 为高级权限 diff --git a/app/src/main/java/com/gh/gamecenter/entity/PersonalEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/PersonalEntity.kt index 064dc16061..30d11efd49 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/PersonalEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/PersonalEntity.kt @@ -41,7 +41,7 @@ data class PersonalEntity( @SerializedName("today_visit") val todayVisit: Int? = 0) : Parcelable { - fun getQaCount() = answer + question + communityArticle + fun getTotalCount() = video + answer + question + communityArticle } } diff --git a/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt index f7f80ffadc..6ba18307ab 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt @@ -3,10 +3,7 @@ package com.gh.gamecenter.entity import android.os.Parcelable import com.gh.common.annotation.SyncPage import com.gh.common.syncpage.SyncFieldConstants -import com.gh.gamecenter.qa.entity.AnswerEntity -import com.gh.gamecenter.qa.entity.CommunityVideoEntity -import com.gh.gamecenter.qa.entity.ImageInfo -import com.gh.gamecenter.qa.entity.Questions +import com.gh.gamecenter.qa.entity.* import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.IgnoredOnParcel import kotlinx.android.parcel.Parcelize @@ -18,6 +15,12 @@ data class PersonalHistoryEntity( val type: String = "", var question: Question = Question(), var brief: String = "", + var des: String = "", + var url: String = "", + @SerializedName("video_info") + var videoInfo: VideoInfo = VideoInfo(), + var poster: String = "", + var length: Long = 0, var count: Count = Count(), val time: Long = 0, var title: String = "", @@ -60,7 +63,16 @@ data class PersonalHistoryEntity( var vote: Int = -1, @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COUNT]) var answer: Int = -1, - var reply: Int = -1) : Parcelable + var reply: Int = -1) : Parcelable { + + fun transformForumCount(): com.gh.gamecenter.qa.entity.Count { + val count = com.gh.gamecenter.qa.entity.Count() + count.comment = comment + count.vote = vote + count.answer = answer + return count + } + } @Parcelize data class Comment( @@ -88,7 +100,10 @@ data class PersonalHistoryEntity( @SerializedName("ori_icon") var rawIcon: String? = null, @SerializedName("icon_subscript") - var iconSubscript: String? = null + var iconSubscript: String? = null, + + @SerializedName("played_time") + val playedTime: Long = 0 ) : Parcelable { @IgnoredOnParcel var name: String @@ -113,14 +128,11 @@ data class PersonalHistoryEntity( answer.images = images answer.videos = videos answer.time = time - answer.vote = count.vote - answer.commentCount = count.comment + answer.count = count.transformForumCount() answer.me = me answer.commentable = commentable answer.articleCommunityId = community.id - if (type.contains("article")) { - answer.type = "community_article" - } + answer.type = type return answer } @@ -133,4 +145,27 @@ data class PersonalHistoryEntity( question.communityName = community.name return question } + + fun transformForumVideoEntity(): ForumVideoEntity { + val forumVideoEntity = ForumVideoEntity() + if (type == "video") { + forumVideoEntity.id = id + forumVideoEntity.title = title + forumVideoEntity.des = des + forumVideoEntity.url = url + forumVideoEntity.poster = poster + forumVideoEntity.length = length + forumVideoEntity.videoInfo = videoInfo + } else { + forumVideoEntity.title = title + forumVideoEntity.des = brief + if (getPassVideos().isNotEmpty()) { + forumVideoEntity.id = getPassVideos()[0].id + forumVideoEntity.url = getPassVideos()[0].url + forumVideoEntity.poster = getPassVideos()[0].poster + forumVideoEntity.videoInfo = VideoInfo(width = getPassVideos()[0].width, height = getPassVideos()[0].height) + } + } + return forumVideoEntity + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/VideoDescItemEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/VideoDescItemEntity.kt new file mode 100644 index 0000000000..3670d1afbe --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/VideoDescItemEntity.kt @@ -0,0 +1,6 @@ +package com.gh.gamecenter.entity + +data class VideoDescItemEntity( + var topVideoInfo: ForumVideoEntity? = null, + var recommendVideo: ForumVideoEntity? = null +) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/VideoDraftEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/VideoDraftEntity.kt index 96e0f97163..d6f0d06189 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/VideoDraftEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/VideoDraftEntity.kt @@ -8,6 +8,7 @@ import kotlinx.android.parcel.Parcelize class VideoDraftEntity(@SerializedName("_id") val id: String = "", val title: String = "", + val des: String = "", var poster: String = "", val url: String = "", @SerializedName("game_id") @@ -15,21 +16,26 @@ class VideoDraftEntity(@SerializedName("_id") @SerializedName("game_name") val gameName: String = "", @SerializedName("category_id") - val categoryId: String = "", + val categoryId: String? = "", val size: Long = 0, val length: Long = 0, @SerializedName("tag_id") - val tagsId: List = ArrayList(), + val tagsId: List? = ArrayList(), val user: UserEntity = UserEntity(), val time: VideoTimeEntity = VideoTimeEntity(), @SerializedName("local_path") - var localPath: String = "", + var localPath: String? = "", val original: String = "",//是否原创 //是否原创 yes/no 默认为空字符串 val source: String = "",//转载来源, 当 original=yes @SerializedName("tag_activity_name") val tagActivityName: String = "", @SerializedName("tag_activity_id") - var tagActivityId: String = "") : Parcelable + var tagActivityId: String = "", + var type: String = "", + var game: GameEntity? = null, + @SerializedName("bbs_id") + var bbsId: String = "", + var bbs: CommunityEntity? = null) : Parcelable @Parcelize class VideoTimeEntity(val create: Long = 0, diff --git a/app/src/main/java/com/gh/gamecenter/eventbus/EBForumRecordChange.java b/app/src/main/java/com/gh/gamecenter/eventbus/EBForumRecordChange.java new file mode 100644 index 0000000000..1d8f86c241 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/eventbus/EBForumRecordChange.java @@ -0,0 +1,20 @@ +package com.gh.gamecenter.eventbus; + +import com.gh.gamecenter.entity.ForumEntity; + +public class EBForumRecordChange { + + ForumEntity forumEntity; + + public EBForumRecordChange(ForumEntity forumEntity) { + this.forumEntity = forumEntity; + } + + public ForumEntity getForumEntity() { + return forumEntity; + } + + public void setForumEntity(ForumEntity forumEntity) { + this.forumEntity = forumEntity; + } +} diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt index 12441cc748..291c7c8bfd 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt @@ -10,7 +10,6 @@ import com.gh.base.BaseActivity import com.gh.common.constant.ItemViewType import com.gh.common.syncpage.ISyncAdapterHandler import com.gh.common.util.MtaHelper -import com.gh.common.util.dip2px import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter @@ -20,7 +19,8 @@ import com.gh.gamecenter.forum.home.ForumArticleAskItemViewHolder import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.Questions -import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntrance: String, val path: String) : ListAdapter(context), ISyncAdapterHandler { @@ -56,10 +56,11 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra questions.id = answer.id ?: "" questions.title = answer.articleTitle questions.description = answer.description - questions.answerCount = answer.answerCount + questions.answerCount = answer.count.answer answer.questions = questions if (path == "精华") answer.type = "community_article" if (path == "问答") answer.type = "question" + if (path == "视频") answer.type = "video" if (answer.bbs.id.isEmpty()) answer.bbs.id = bbsId @@ -76,12 +77,20 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra answerViewHolder.bindForumAnswerItem(answer, mEntrance, path) answerViewHolder.itemView.setOnClickListener { val entrance = BaseActivity.mergeEntranceAndPath(mEntrance, path) - if ("community_article" == answer.type) { - MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") - mContext.startActivity(ArticleDetailActivity.getIntent(mContext, CommunityEntity(bbsId), answer.id!!, mEntrance, path)) - } else { - MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, answer.id, mEntrance, path)) + when (answer.type) { + "community_article" -> { + MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") + mContext.startActivity(ArticleDetailActivity.getIntent(mContext, CommunityEntity(bbsId), answer.id!!, mEntrance, path)) + } + "video" -> { + MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") + mContext.startActivity(ForumVideoDetailActivity.getIntent(mContext, answer.id ?:"")) + } + else -> { + MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, answer.id + ?: "", mEntrance, path)) + } } } } diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt index 8ce88b45c4..f6332492df 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt @@ -4,15 +4,16 @@ import android.os.Bundle import android.view.View import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView -import com.gh.common.util.EntranceUtils -import com.gh.common.util.dip2px -import com.gh.common.util.viewModelProvider +import com.gh.common.AppExecutor +import com.gh.common.constant.Constants +import com.gh.common.util.* import com.gh.common.view.divider.HorizontalDividerItemDecoration import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.eventbus.EBDeleteDetail import com.gh.gamecenter.eventbus.EBTypeChange +import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper import com.gh.gamecenter.qa.CommunityFragment import com.gh.gamecenter.qa.entity.AnswerEntity import org.greenrobot.eventbus.EventBus @@ -23,52 +24,16 @@ class ForumArticleAskListFragment : ListFragment CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { - EventBus.getDefault().post(EBTypeChange(ForumDetailFragment.EB_HIDE_QUESTION_BUTTON, 0)) - } else if (dy < -CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { - EventBus.getDefault().post(EBTypeChange(ForumDetailFragment.EB_SHOW_QUESTION_BUTTON, 0)) - } - } - }) - } - - fun refresh() { - onLoadRefresh() - } - - override fun hideRefreshingLayout() { - (parentFragment as? ForumDetailFragment)?.hideRefreshingLayout() - } - - fun onRefresh(filter: String) { - mViewModel?.sort = if (filter == "最新发布") "time.create" else "time.reply" - onRefresh() - } - - override fun provideListAdapter(): ListAdapter<*> { if (!::mAdapter.isInitialized) { mAdapter = ForumArticleAskListAdapter(requireContext(), bbsId, mEntrance, mPath) @@ -83,6 +48,124 @@ class ForumArticleAskListFragment : ListFragment CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { + EventBus.getDefault().post(EBTypeChange(ForumDetailFragment.EB_HIDE_QUESTION_BUTTON, 0)) + } else if (dy < -CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { + EventBus.getDefault().post(EBTypeChange(ForumDetailFragment.EB_SHOW_QUESTION_BUTTON, 0)) + } + scroll() + } + }) + } + + override fun onResume() { + resumeVideo() + super.onResume() + } + + override fun onPause() { + super.onPause() + pauseVideo() + } + + override fun onDestroy() { + super.onDestroy() + mScrollCalculatorHelper?.currentPlayer?.release() + } + + override fun onLoadDone() { + super.onLoadDone() + AppExecutor.uiExecutor.executeWithDelay(Runnable { + scroll() + mScrollCalculatorHelper?.onScrollStateChanged(mListRv, RecyclerView.SCROLL_STATE_IDLE) + }, 100) + } + + fun refresh() { + onLoadRefresh() + } + + override fun hideRefreshingLayout() { + (parentFragment as? ForumDetailFragment)?.hideRefreshingLayout() + } + + fun onRefresh(filter: String) { + if (mPath == "全部") { + mViewModel?.sort = if (filter == "最新发布") "time.create" else "time.reply" + } + if (mPath == "视频") { + mViewModel?.videoSort = if (filter == "推荐") "recommend" else "time.upload" + } + onRefresh() + } + + private fun scroll() { + val firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition() + val lastVisibleItem = mLayoutManager.findLastVisibleItemPosition() + mScrollCalculatorHelper?.onScroll(mViewModel?.videoList, firstVisibleItem, lastVisibleItem) + } + + private fun resumeVideo() { + mScrollCalculatorHelper?.run { + if (currentPlayer != null) { + val video = mViewModel?.videoList?.safelyGetInRelease(currentPosition) + if (video != null) { + val position = ForumScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(video.url)) + //这里必须要延迟操作,否则会白屏 + mBaseHandler.postDelayed({ + if (position != 0L) { + currentPlayer?.seekTo(position) + currentPlayer?.onVideoResume(false) + val videoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + if (videoVoiceStatus) { + currentPlayer?.mute() + } else { + currentPlayer?.unMute() + } + } else { + currentPlayer?.release() + } + + }, 50) + } + } + } + } + + private fun pauseVideo() { + mScrollCalculatorHelper?.run { + if (currentPosition >= 0) { + currentPlayer?.onVideoPause() + val position = currentPlayer?.getCurrentPosition() ?: 0L + val video = mViewModel?.videoList?.safelyGetInRelease(currentPosition) + if (video != null) { + ForumScrollCalculatorHelper.savePlaySchedule(MD5Utils.getContentMD5(video.url), position) + } + } + } + } + override fun addSyncPageObserver(): Boolean = true override fun provideSyncAdapter(): ForumArticleAskListAdapter = mAdapter diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt index b06b8752ec..de270e6933 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.gh.common.util.UrlFilterUtils import com.gh.gamecenter.baselist.ListViewModel -import com.gh.gamecenter.forum.home.ForumArticleListViewModel +import com.gh.gamecenter.entity.ForumVideoEntity import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp @@ -14,6 +14,8 @@ import io.reactivex.Observable class ForumArticleAskListViewModel(application: Application, val bbsId: String = "", val mPath: String = "") : ListViewModel(application) { var sort: String = "time.reply" + var videoSort: String = "recommend" + var videoList = listOf() override fun provideDataObservable(page: Int): Observable> { return when (mPath) { @@ -23,14 +25,25 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String = "精华" -> { RetrofitManager.getInstance(getApplication()).api.getEssenceForumList(bbsId, page) } - else -> { + "问答" -> { RetrofitManager.getInstance(getApplication()).api.getAskForumList(bbsId, page) } + else -> { + RetrofitManager.getInstance(getApplication()).api.getVideoForumList(bbsId, UrlFilterUtils.getFilterQuery(videoSort, "-1"), page) + } } } override fun mergeResultLiveData() { - mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } + mResultLiveData.addSource(mListLiveData) { list -> + + videoList = list.map { + if (mPath == "视频") it.type = "video" + it.transformForumVideoEntity() + } + + mResultLiveData.postValue(list) + } } class Factory(private val bbsId: String, val path: String) : ViewModelProvider.NewInstanceFactory() { diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt index 7b3d09ea38..7c7e8706d4 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt @@ -1,6 +1,8 @@ package com.gh.gamecenter.forum.detail +import android.graphics.Bitmap import android.graphics.drawable.ColorDrawable +import android.os.Build import android.os.Bundle import android.view.Gravity import android.view.LayoutInflater @@ -10,7 +12,7 @@ import android.view.animation.AnimationUtils import android.widget.LinearLayout import android.widget.PopupWindow import android.widget.TextView -import androidx.constraintlayout.widget.ConstraintLayout +import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.core.view.ViewCompat @@ -24,9 +26,11 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder import com.facebook.drawee.generic.RoundingParams import com.facebook.drawee.view.SimpleDraweeView import com.gh.base.fragment.BaseLazyTabFragment +import com.gh.common.constant.Constants import com.gh.common.dialog.TrackableDialog import com.gh.common.util.* import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.databinding.FragmentForumDetailBinding import com.gh.gamecenter.entity.CommunityEntity @@ -40,8 +44,10 @@ import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity import com.gh.gamecenter.gamedetail.GameDetailFragment import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mvvm.Status +import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.article.edit.ArticleEditActivity import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity +import com.gh.gamecenter.qa.video.publish.VideoPublishActivity import com.google.android.material.appbar.AppBarLayout import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -53,12 +59,14 @@ class ForumDetailFragment : BaseLazyTabFragment() { private var mAllForumArticleAskListFragment: ForumArticleAskListFragment? = null private var mEssenceForumArticleAskListFragment: ForumArticleAskListFragment? = null private var mAskForumArticleAskListFragment: ForumArticleAskListFragment? = null + private var mVideoForumArticleAskListFragment: ForumArticleAskListFragment? = null private var bbsId: String = "" private var mViewModel: ForumDetailViewModel? = null private var mForumDetail: ForumDetailEntity? = null private lateinit var mBinding: FragmentForumDetailBinding private var mFilter = "最新回复" + private var mVideoFilter = "推荐" private var mLastPosition = 0 override fun initFragmentList(fragments: MutableList) { @@ -68,15 +76,19 @@ class ForumDetailFragment : BaseLazyTabFragment() { EntranceUtils.KEY_PATH to "精华", EntranceUtils.KEY_BBS_ID to bbsId)) as ForumArticleAskListFragment? mAskForumArticleAskListFragment = ForumArticleAskListFragment().with(bundleOf(EntranceUtils.KEY_ENTRANCE to "论坛详情", EntranceUtils.KEY_PATH to "问答", EntranceUtils.KEY_BBS_ID to bbsId)) as ForumArticleAskListFragment? + mVideoForumArticleAskListFragment = ForumArticleAskListFragment().with(bundleOf(EntranceUtils.KEY_ENTRANCE to "论坛详情", + EntranceUtils.KEY_PATH to "视频", EntranceUtils.KEY_BBS_ID to bbsId)) as ForumArticleAskListFragment? fragments.add(mAllForumArticleAskListFragment!!) fragments.add(mEssenceForumArticleAskListFragment!!) fragments.add(mAskForumArticleAskListFragment!!) + fragments.add(mVideoForumArticleAskListFragment!!) } override fun initTabTitleList(tabTitleList: MutableList) { tabTitleList.add(TAB_TITLE_ALL) tabTitleList.add(TAB_TITLE_ESSENCE) tabTitleList.add(TAB_TITLE_ASK) + tabTitleList.add(TAB_TITLE_VIDEO) } override fun getInflatedLayout(): View { @@ -158,6 +170,7 @@ class ForumDetailFragment : BaseLazyTabFragment() { }) mViewPager.doOnPageSelected { mBinding.filterContainer.goneIf(it != 0) + mBinding.videoFilterContainer.goneIf(it != 3) MtaHelper.onEvent("论坛详情", getKeyValue(mLastPosition), getKeyValue(it)) mLastPosition = it } @@ -171,6 +184,9 @@ class ForumDetailFragment : BaseLazyTabFragment() { if (mAskForumArticleAskListFragment != null && mViewPager.currentItem == 2) { mAskForumArticleAskListFragment?.refresh() } + if (mVideoForumArticleAskListFragment != null && mViewPager.currentItem == 3) { + mVideoForumArticleAskListFragment?.refresh() + } } LogUtils.uploadAccessToBbs(bbsId, "论坛详情") } @@ -190,8 +206,14 @@ class ForumDetailFragment : BaseLazyTabFragment() { private fun initUI() { mForumDetail?.apply { mBinding.entity = this - mBinding.forumThumbSmall.displayGameIcon(game.getIcon(), game.iconSubscript) - mBinding.forumThumbBig.displayGameIcon(game.getIcon(), game.iconSubscript) + if (type == "official_bbs") { + mBinding.forumThumbSmall.displayGameIcon(icon, null) + mBinding.forumThumbBig.displayGameIcon(icon, null) + } else { + mBinding.forumThumbSmall.displayGameIcon(game.getIcon(), game.iconSubscript) + mBinding.forumThumbBig.displayGameIcon(game.getIcon(), game.iconSubscript) + } + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) initModeratorHeadView(moderator) if (topLink.isNotEmpty()) { mBinding.forumTopContentRv.visibility = View.VISIBLE @@ -200,38 +222,32 @@ class ForumDetailFragment : BaseLazyTabFragment() { adapter = ForumTopLinkAdapter(requireContext(), topLink) } } else { - if (background.isNotEmpty()) { - val params = mBinding.tabContainer.layoutParams as LinearLayout.LayoutParams - params.topMargin = DisplayUtils.dip2px(-8f) - mBinding.tabContainer.layoutParams = params - mBinding.tabContainer.background = ContextCompat.getDrawable(requireContext(), R.drawable.background_shape_white_radius_5_top_only) - } + val params = mBinding.tabContainer.layoutParams as LinearLayout.LayoutParams + params.topMargin = DisplayUtils.dip2px(-8f) + mBinding.tabContainer.layoutParams = params + mBinding.tabContainer.background = ContextCompat.getDrawable(requireContext(), R.drawable.background_shape_white_radius_5_top_only) } + if (background.isEmpty()) { - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) - mBinding.forumBackground.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.white)) - mBinding.forumNameTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_333333)) - mBinding.followTv.background = ContextCompat.getDrawable(requireContext(), if (me.isFollower) R.drawable.bg_shape_f5_radius_999 else R.drawable.bg_forum_follow) - mBinding.followTv.setTextColor(ContextCompat.getColor(requireContext(), if (me.isFollower) R.color.text_999999 else R.color.theme_font)) + ImageUtils.getBitmap(game.getIcon(), object : BiCallback { + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + override fun onFirst(first: Bitmap) { + var blurBitmap = BitmapUtils.getBlurBitmap(requireContext(), first, 25) + mBinding.forumDefaultBackground.setImageBitmap(blurBitmap) + } - val params = mBinding.forumThumbBig.layoutParams as ConstraintLayout.LayoutParams - params.bottomMargin = DisplayUtils.dip2px(20f) - mBinding.forumThumbBig.layoutParams = params - - val headContainerParams = mBinding.headContainer.layoutParams as LinearLayout.LayoutParams - headContainerParams.height = DisplayUtils.dip2px(186f) - mBinding.headContainer.layoutParams = headContainerParams + override fun onSecond(second: Boolean) {} + }) } else { - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) ImageUtils.display(mBinding.forumBackground, background) - mBinding.forumNameTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) - mBinding.followTv.setTextColor(ContextCompat.getColor(requireContext(), if (me.isFollower) R.color.white_alpha_40 else R.color.white_alpha_90)) - mBinding.followTv.background = ContextCompat.getDrawable(requireContext(), R.drawable.bg_forum_detail_follow) } + mBinding.followTv.setTextColor(ContextCompat.getColor(requireContext(), if (me.isFollower) R.color.text_999999 else R.color.white)) + mBinding.followTv.background = ContextCompat.getDrawable(requireContext(), if (me.isFollower) R.drawable.bg_shape_f5_radius_999 else R.drawable.login_btn_bg) } } - @OnClick(R.id.filterContainer, R.id.community_edit, R.id.moderatorTv, R.id.reuse_no_connection, R.id.gameZoneTv, R.id.followTv, R.id.forumThumbBig, R.id.searchIv) + @OnClick(R.id.filterContainer, R.id.videoFilterContainer, R.id.community_edit, R.id.moderatorTv, R.id.reuse_no_connection, R.id.gameZoneTv, + R.id.followTv, R.id.forumThumbBig, R.id.searchIv, R.id.forumRuleContainer) fun onViewClick(view: View) { when (view.id) { R.id.searchIv -> { @@ -239,7 +255,7 @@ class ForumDetailFragment : BaseLazyTabFragment() { } R.id.filterContainer -> { MtaHelper.onEvent("论坛详情", "全部Tab", "过滤选项") - showFilterPopupWindow { + showFilterPopupWindow(arrayListOf("最新发布", "最新回复")) { mFilter = it mBinding.forumFilter.text = mFilter if (mAllForumArticleAskListFragment != null && mViewPager.currentItem == 0) { @@ -247,6 +263,16 @@ class ForumDetailFragment : BaseLazyTabFragment() { } } } + R.id.videoFilterContainer -> { + MtaHelper.onEvent("论坛详情", "视频Tab", "过滤选项") + showFilterPopupWindow(arrayListOf("推荐", "最新发布")) { + mVideoFilter = it + mBinding.videoForumFilter.text = mVideoFilter + if (mVideoForumArticleAskListFragment != null && mViewPager.currentItem == 3) { + mVideoForumArticleAskListFragment?.onRefresh(mVideoFilter) + } + } + } R.id.community_edit -> { showCommunityEditWindow() } @@ -297,6 +323,10 @@ class ForumDetailFragment : BaseLazyTabFragment() { } } } + + R.id.forumRuleContainer -> { + startActivity(NewsDetailActivity.getIntentById(requireContext(), Constants.FORUM_REGULATIONS_NEWS_ID, "论坛详情")) + } } } @@ -304,16 +334,14 @@ class ForumDetailFragment : BaseLazyTabFragment() { mBinding.titleTv.setTextColor(ContextCompat.getColor(requireContext(), if (isToolbarWhite) R.color.black else R.color.white)) mBinding.toolbar.setBackgroundColor(ContextCompat.getColor(requireContext(), if (isToolbarWhite) R.color.white else R.color.transparent)) DisplayUtils.setStatusBarColor(requireActivity(), if (isToolbarWhite) R.color.white else R.color.transparent, true) - if (mForumDetail?.background?.isEmpty() == false) { - if (isToolbarWhite) { - DisplayUtils.setLightStatusBar(requireActivity(), true) - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) - mBinding.searchIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_forum_detail_search)) - } else { - DisplayUtils.setLightStatusBar(requireActivity(), false) - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) - mBinding.searchIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_forum_detail_search_light)) - } + if (isToolbarWhite) { + DisplayUtils.setLightStatusBar(requireActivity(), true) + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) + mBinding.searchIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_forum_detail_search)) + } else { + DisplayUtils.setLightStatusBar(requireActivity(), false) + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + mBinding.searchIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_forum_detail_search_light)) } } @@ -333,7 +361,7 @@ class ForumDetailFragment : BaseLazyTabFragment() { .setBackground(ColorDrawable(ContextCompat.getColor(requireContext(), R.color.placeholder_bg))) .setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP) .build() - val params = LinearLayout.LayoutParams(24f.dip2px(), 24f.dip2px()) + val params = LinearLayout.LayoutParams(20f.dip2px(), 20f.dip2px()) headView.layoutParams = params if (index != 0) { params.leftMargin = (-8f).dip2px() @@ -349,8 +377,7 @@ class ForumDetailFragment : BaseLazyTabFragment() { } - private fun showFilterPopupWindow(clickListener: (String) -> Unit) { - val contentList = arrayListOf("最新发布", "最新回复") + private fun showFilterPopupWindow(contentList: ArrayList, clickListener: (String) -> Unit) { val inflater = LayoutInflater.from(requireContext()) val layout = inflater.inflate(R.layout.layout_popup_container, null) val popupWindow = PopupWindow( @@ -368,7 +395,7 @@ class ForumDetailFragment : BaseLazyTabFragment() { val container = layout.findViewById(R.id.container) for (text in contentList) { val item = inflater.inflate(R.layout.layout_popup_option_item, container, false) - if (mFilter == text) { + if (mVideoFilter == text) { (item as TextView).setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font)) } else { (item as TextView).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_999999)) @@ -408,24 +435,39 @@ class ForumDetailFragment : BaseLazyTabFragment() { window?.setWindowAnimations(R.style.community_publication_animation) dialog.setContentView(contentView, params) dialog.show() - contentView.findViewById(R.id.community_edit_article).setOnClickListener { + contentView.findViewById(R.id.community_edit_article_container).setOnClickListener { context?.ifLogin("论坛详情", action = { checkStoragePermissionBeforeAction { MtaHelper.onEvent("论坛详情", "发布", "发帖子") startActivity(ArticleEditActivity.getIntent(requireContext(), CommunityEntity(mForumDetail?.id ?: "", mForumDetail?.name - ?: "", icon = mForumDetail?.game?.getIcon(), iconSubscript = mForumDetail?.game?.iconSubscript))) + ?: "", icon = mForumDetail?.game?.getIcon(), iconSubscript = mForumDetail?.game?.iconSubscript), mForumDetail?.type + ?: "")) dialog.dismiss() } }) } - contentView.findViewById(R.id.community_edit_question).setOnClickListener { + contentView.findViewById(R.id.community_edit_question_container).setOnClickListener { context?.ifLogin("论坛详情", action = { checkStoragePermissionBeforeAction { MtaHelper.onEvent("论坛详情", "发布", "提问") startActivity(QuestionEditActivity.getIntent(requireContext(), CommunityEntity(mForumDetail?.id ?: "", mForumDetail?.name - ?: "", icon = mForumDetail?.game?.getIcon(), iconSubscript = mForumDetail?.game?.iconSubscript))) + ?: "", icon = mForumDetail?.game?.getIcon(), iconSubscript = mForumDetail?.game?.iconSubscript), mForumDetail?.type + ?: "")) + dialog.dismiss() + } + }) + } + contentView.findViewById(R.id.community_edit_video_container).setOnClickListener { + context?.ifLogin("论坛详情", action = { + checkStoragePermissionBeforeAction { + MtaHelper.onEvent("论坛详情", "发布", "发视频") + val communityEntity = CommunityEntity(mForumDetail?.id + ?: "", mForumDetail?.name + ?: "", icon = mForumDetail?.game?.getIcon(), iconSubscript = mForumDetail?.game?.iconSubscript) + startActivity(VideoPublishActivity.getIntent(requireContext(), communityEntity, mForumDetail?.type + ?: "", mEntrance, "论坛详情")) dialog.dismiss() } }) @@ -461,6 +503,7 @@ class ForumDetailFragment : BaseLazyTabFragment() { const val TAB_TITLE_ALL = "全部" const val TAB_TITLE_ESSENCE = "精华" const val TAB_TITLE_ASK = "问答" + const val TAB_TITLE_VIDEO = "视频" const val EB_SHOW_QUESTION_BUTTON = "EB_SHOW_QUESTION_BUTTON" const val EB_HIDE_QUESTION_BUTTON = "EB_HIDE_QUESTION_BUTTON" } diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt index 1de25fe3ff..e6600f6948 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt @@ -6,26 +6,40 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.gh.common.util.singleToMain import com.gh.gamecenter.entity.ForumDetailEntity +import com.gh.gamecenter.eventbus.EBForumRecordChange import com.gh.gamecenter.mvvm.Resource import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.EmptyResponse import com.gh.gamecenter.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.room.AppDatabase import com.halo.assistant.HaloApp import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import okhttp3.ResponseBody +import org.greenrobot.eventbus.EventBus import retrofit2.HttpException class ForumDetailViewModel(application: Application, val bbsId: String) : AndroidViewModel(application) { private val mApi = RetrofitManager.getInstance(application).api + private val mForumDao = AppDatabase.getInstance(HaloApp.getInstance()).forumDao() var forumDetail = MutableLiveData>() init { + postForumRead() getForumDetail() } + @SuppressLint("CheckResult") + fun postForumRead() { + mApi.postForumRead(bbsId) + .compose(singleToMain()) + .subscribe(EmptyResponse()) + } + fun getForumDetail() { mApi.getForumDetail(bbsId) .subscribeOn(Schedulers.io()) @@ -34,6 +48,10 @@ class ForumDetailViewModel(application: Application, val bbsId: String) : Androi override fun onResponse(response: ForumDetailEntity?) { super.onResponse(response) forumDetail.postValue(Resource.success(response)) + response?.run { + mForumDao.addForum(convertForumDetailEntityToForumEntity()) + EventBus.getDefault().post(EBForumRecordChange(convertForumDetailEntityToForumEntity())) + } } override fun onFailure(e: HttpException?) { diff --git a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowAdapter.kt deleted file mode 100644 index 8c493c522d..0000000000 --- a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowAdapter.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.gh.gamecenter.forum.follow - -import android.content.Context -import android.view.Gravity -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.gh.base.BaseRecyclerViewHolder -import com.gh.common.util.* -import com.gh.common.view.BugFixedPopupWindow -import com.gh.gamecenter.R -import com.gh.gamecenter.databinding.ForumMyFollowBinding -import com.gh.gamecenter.entity.ForumEntity -import com.gh.gamecenter.eventbus.EBForumFollowChange -import com.gh.gamecenter.forum.detail.ForumDetailActivity -import com.gh.gamecenter.qa.entity.CommunitySelectEntity -import com.lightgame.adapter.BaseRecyclerAdapter -import org.greenrobot.eventbus.EventBus - -class ForumMyFollowAdapter(context: Context, val mViewModel: ForumMyFollowViewModel) : BaseRecyclerAdapter(context) { - val entityList = ArrayList() - - fun setListData(updateData: List) { - entityList.clear() - entityList.addAll(updateData) - notifyDataSetChanged() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return ForumMyFollowViewHolder(ForumMyFollowBinding.bind(mLayoutInflater.inflate(R.layout.forum_my_follow, parent, false))) - } - - override fun getItemCount(): Int = entityList.size - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - val forumEntity = entityList[position] - val followViewHolder = holder as ForumMyFollowViewHolder - followViewHolder.binding.entity = forumEntity - followViewHolder.binding.forumIcon.displayGameIcon(forumEntity.game.getIcon(), forumEntity.game.iconSubscript) - followViewHolder.binding.moreActionIv.setOnClickListener { - MtaHelper.onEvent("论坛首页", "我关注的论坛", "更多") - showMoreWindow(it, forumEntity) - } - followViewHolder.itemView.setOnClickListener { - MtaHelper.onEvent("论坛首页", "我关注的论坛", forumEntity.name) - mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, "我的关注")) - } - } - - private fun showMoreWindow(view: View, entity: ForumEntity) { - val inflater = LayoutInflater.from(mContext) - val layout = inflater.inflate(R.layout.layout_popup_container, null) - val popupWindow = BugFixedPopupWindow(layout, - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT) - val container = layout.findViewById(R.id.container) - val dialogOptions = arrayListOf("取消关注") - for (text in dialogOptions) { - val item = inflater.inflate(R.layout.layout_popup_option_item, container, false) - container.addView(item) - - val hitText = item.findViewById(R.id.hint_text) - hitText.text = text - - item.setOnClickListener { - popupWindow.dismiss() - when (text) { - "取消关注" -> { - DialogUtils.showNewAlertDialog(mContext, "提示", "确定取消关注", "暂不", "确定", null, Gravity.CENTER, false, {}, { - MtaHelper.onEvent("论坛首页", "我关注的论坛", "取消关注") - mViewModel.unFollowForum(entity.id) { - EventBus.getDefault().post(EBForumFollowChange(entity, false)) - } - }) - } - } - } - } - popupWindow.isTouchable = true - popupWindow.isFocusable = true - - popupWindow.showAutoOrientation(view) - } - - class ForumMyFollowViewHolder(val binding: ForumMyFollowBinding) : BaseRecyclerViewHolder(binding.root) -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowFragment.kt deleted file mode 100644 index ef0538645e..0000000000 --- a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowFragment.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.gh.gamecenter.forum.follow - -import android.os.Bundle -import android.view.View -import android.widget.LinearLayout -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.gh.common.AppExecutor -import com.gh.common.util.viewModelProvider -import com.gh.gamecenter.R -import com.gh.gamecenter.eventbus.EBForumFollowChange -import com.gh.gamecenter.normal.NormalFragment -import kotterknife.bindView -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode - -class ForumMyFollowFragment : NormalFragment() { - - val mRefresh by bindView(R.id.list_refresh) - val mListRv by bindView(R.id.list_rv) - val mReuseLoading by bindView(R.id.reuse_ll_loading) - val mNoConnection by bindView(R.id.reuse_no_connection) - val mNoneData by bindView(R.id.reuse_none_data) - - private var mAdapter: ForumMyFollowAdapter? = null - private var mViewModel: ForumMyFollowViewModel? = null - - override fun getLayoutId(): Int = R.layout.fragment_list_base - - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - mViewModel = viewModelProvider() - mListRv.apply { - layoutManager = LinearLayoutManager(requireContext()) - mAdapter = ForumMyFollowAdapter(requireContext(), mViewModel!!) - adapter = mAdapter - } - mViewModel?.forumData?.observe(viewLifecycleOwner, Observer { - mRefresh.isRefreshing = false - mReuseLoading.visibility = View.GONE - if (it != null) { - mNoConnection.visibility = View.GONE - if (it.isNotEmpty()) { - mNoneData.visibility = View.GONE - mAdapter?.setListData(it) - } else { - mNoneData.visibility = View.VISIBLE - } - } else { - mNoneData.visibility = View.GONE - mNoConnection.visibility = View.VISIBLE - } - }) - mNoConnection.setOnClickListener { - mViewModel?.loadFollowsForum() - } - mRefresh.setOnRefreshListener { - mViewModel?.loadFollowsForum() - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onFollowForumChange(forumFollowChange: EBForumFollowChange) { - val entityList = mAdapter?.entityList ?: arrayListOf() - val index = entityList.indexOfFirst { it.id == forumFollowChange.forumEntity.id } - val findEntity = entityList.find { it.id == forumFollowChange.forumEntity.id } - if (!forumFollowChange.isFollow) { - entityList.remove(findEntity) - mAdapter?.notifyItemRangeRemoved(index, 1) - } - if (mAdapter?.entityList?.isEmpty() == true) { - mListRv.visibility = View.GONE - mNoneData.visibility = View.VISIBLE - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowViewModel.kt deleted file mode 100644 index 2876855c76..0000000000 --- a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowViewModel.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.gh.gamecenter.forum.follow - -import android.annotation.SuppressLint -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.MediatorLiveData -import com.gh.gamecenter.baselist.ListViewModel -import com.gh.gamecenter.entity.ForumEntity -import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.qa.entity.CommunitySelectEntity -import com.gh.gamecenter.retrofit.BiResponse -import com.gh.gamecenter.retrofit.Response -import com.gh.gamecenter.retrofit.RetrofitManager -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers -import okhttp3.ResponseBody -import retrofit2.HttpException - -class ForumMyFollowViewModel(application: Application) : AndroidViewModel(application) { - - val forumData = MediatorLiveData>() - init { - loadFollowsForum() - } - - fun loadFollowsForum(){ - RetrofitManager.getInstance(getApplication()).api - .getFollowsForum(UserManager.getInstance().userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response>() { - override fun onResponse(response: List?) { - super.onResponse(response) - forumData.postValue(response) - } - - override fun onFailure(e: HttpException?) { - super.onFailure(e) - forumData.postValue(null) - } - }) - } - - @SuppressLint("CheckResult") - fun unFollowForum(bbsId: String, onSuccess: () -> Unit) { - RetrofitManager.getInstance(getApplication()).api - .unFollowForum(bbsId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { - onSuccess.invoke() - } - }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ArticleItemVideoView.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ArticleItemVideoView.kt new file mode 100644 index 0000000000..826479818d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ArticleItemVideoView.kt @@ -0,0 +1,353 @@ +package com.gh.gamecenter.forum.home + +import android.app.Activity +import android.content.Context +import android.util.AttributeSet +import android.view.Surface +import android.view.View +import android.widget.ImageView +import android.widget.SeekBar +import android.widget.TextView +import androidx.core.content.ContextCompat +import com.facebook.drawee.view.SimpleDraweeView +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.video.detail.CustomManager +import com.lightgame.utils.Utils +import com.shuyu.gsyvideoplayer.utils.CommonUtil +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge +import io.reactivex.disposables.Disposable +import java.util.* + +class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : StandardGSYVideoPlayer(context, attrs) { + + private var mVideoEntity: ForumVideoEntity? = null + private var mMuteDisposable: Disposable? = null + private var mIsAutoPlay = false + var uuid = UUID.randomUUID().toString() + + var thumbImage: SimpleDraweeView = findViewById(R.id.thumbImage) + var durationTv: TextView = findViewById(R.id.durationTv) + var completeContainer: View = findViewById(R.id.completeContainer) + var replayTv: TextView = findViewById(R.id.replayTv) + var shareTv: TextView = findViewById(R.id.shareTv) + var volume: ImageView = findViewById(R.id.volume) + var remainingTv: TextView = findViewById(R.id.remainingTv) + var back: ImageView = findViewById(R.id.back) + + override fun getLayoutId(): Int { + return R.layout.layout_article_item_video + } + + init { + post { + volume.setOnClickListener { toggleMute() } + } + + if (mIfCurrentIsFullscreen) { + showBackBtn() + } else { + hideBackBtn() + } + + setBackFromFullScreenListener { + clearFullscreenLayout() + } + } + + override fun getGSYVideoManager(): GSYVideoViewBridge { + CustomManager.getCustomManager(getKey()).initContext(context.applicationContext) + return CustomManager.getCustomManager(getKey()) + } + + fun startPlayLogic(isAutoPlay: Boolean) { + mIsAutoPlay = isAutoPlay + startPlayLogic() + } + + override fun prepareVideo() { + super.prepareVideo() + violenceUpdateMuteStatus() + } + + fun violenceUpdateMuteStatus() { + if (mMuteDisposable != null) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + mMuteDisposable = rxTimer(25) { + if (it >= 400) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + updateMuteStatus() + } + } + + // 不需要弹弹窗,直接播放 + override fun showWifiDialog() { + startPlayLogic(false) + //val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false) + //if (trafficVideo) { + // 不延迟的话 isCacheFile 可能直接返回 false + postDelayed({ + tryCatchInRelease { + // 这个库的 NetworkUtils.isWifiConnected 可能会触发空指针,这里换为我们自己的 + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "网络异常,请检查手机网络状态") + } else if (!NetworkUtils.isWifiConnected(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "当前为非Wi-Fi环境,请注意流量消耗") + } + } + }, 100) + // } + } + + override fun updateStartImage() { + if (mStartButton is ImageView) { + val imageView = mStartButton as ImageView + when (mCurrentState) { + GSYVideoView.CURRENT_STATE_PLAYING -> imageView.setImageResource(R.drawable.icon_preview_video_pause) + GSYVideoView.CURRENT_STATE_ERROR -> imageView.setImageResource(R.drawable.icon_preview_video_play) + else -> imageView.setImageResource(R.drawable.icon_preview_video_play) + } + } + } + + override fun onClickUiToggle() { + if (mCurrentState == CURRENT_STATE_PLAYING) { + if (mStartButton.visibility == View.VISIBLE) { + changeUiToPlayingClear() + } else { + changeUiToPlayingShow() + } + } else { + super.onClickUiToggle() + } + } + + /******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/ + + override fun onSurfaceUpdated(surface: Surface) { + super.onSurfaceUpdated(surface) + if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) { + mThumbImageViewLayout.visibility = View.INVISIBLE + uploadVideoStreamingPlaying("开始播放") + } + } + + //监控播放错误 +// override fun onError(what: Int, extra: Int) { +// super.onError(what, extra) +// Utils.toast(context, "网络错误,视频播放失败") +// setViewShowState(mStartButton, View.INVISIBLE) +//// errorContainer.visibility = View.VISIBLE +// } + + override fun releaseVideos() { + uploadVideoStreamingPlaying("结束播放") + CustomManager.releaseAllVideos(getKey()) + } + + override fun onVideoPause() { + super.onVideoPause() + uploadVideoStreamingPlaying("暂停播放") + } + + // 重载以减少横竖屏切换的时间 + override fun checkoutState() { + removeCallbacks(mCheckoutTask) + postDelayed(mCheckoutTask, 300) + } + + override fun clearFullscreenLayout() { + super.clearFullscreenLayout() + updateMuteStatus() + hideBackBtn() + } + + override fun setProgressAndTime(progress: Int, secProgress: Int, currentTime: Int, totalTime: Int, forceChange: Boolean) { + super.setProgressAndTime(progress, secProgress, currentTime, totalTime, forceChange) + if (remainingTv.visibility == View.VISIBLE) { + remainingTv.text = CommonUtil.stringForTime(totalTime - currentTime) + } + } + + private fun showBackBtn() { + mTopContainer.background = ContextCompat.getDrawable(context, R.drawable.video_title_bg) + back.visibility = View.VISIBLE + } + + private fun hideBackBtn() { + mTopContainer?.setBackgroundResource(0) + back.visibility = View.GONE + } + + fun updateThumb(url: String) { + ImageUtils.display(thumbImage, url) + } + + fun updateDurationTv(duration: String) { + durationTv.text = duration + } + + fun updateVideoData(video: ForumVideoEntity) { + mVideoEntity = video + } + + override fun setViewShowState(view: View?, visibility: Int) { + if (view === mThumbImageViewLayout && visibility != View.VISIBLE) { + return + } + super.setViewShowState(view, visibility) + } + + /********************************各类UI的状态显示*********************************************/ + + override fun touchSurfaceMoveFullLogic(absDeltaX: Float, absDeltaY: Float) { + super.touchSurfaceMoveFullLogic(absDeltaX, absDeltaY) + //不给触摸快进,如果需要,屏蔽下方代码即可 + mChangePosition = false + //不给触摸音量,如果需要,屏蔽下方代码即可 + mChangeVolume = false + //不给触摸亮度,如果需要,屏蔽下方代码即可 + mBrightness = false + } + + override fun touchDoubleUp() { + + } + + override fun setStateAndUi(state: Int) { + super.setStateAndUi(state) + if (currentState == GSYVideoView.CURRENT_STATE_PREPAREING) { + setViewShowState(durationTv, View.GONE) + } + +// if (currentState == GSYVideoView.CURRENT_STATE_PLAYING) { +// setViewShowState(mBottomProgressBar, View.VISIBLE) +// } + + if (currentState == GSYVideoView.CURRENT_STATE_AUTO_COMPLETE) { + hideAllWidget() + setViewShowState(completeContainer, View.VISIBLE) + mTopContainer.visibility = View.VISIBLE + replayTv.setOnClickListener { + startButton.performClick() + violenceUpdateMuteStatus() + uploadVideoStreamingPlaying("重新播放") + } + shareTv.setOnClickListener { + share() + } + } else { + setViewShowState(completeContainer, View.GONE) + } + } + + override fun changeUiToPlayingShow() { + super.changeUiToPlayingShow() + if (!mIfCurrentIsFullscreen) setViewShowState(mBottomProgressBar, View.VISIBLE) + } + + override fun changeUiToPlayingClear() { + super.changeUiToPlayingClear() + if (mIfCurrentIsFullscreen) setViewShowState(mBottomProgressBar, View.GONE) + } + + private fun toggleMute() { + if (mVideoEntity?.videoIsMuted == true) { + unMute(true) + } else { + mute(true) + } + } + + fun updateMuteStatus() { + if (mVideoEntity?.videoIsMuted == true) { + mute() + } else { + unMute() + } + } + + fun mute(isManual: Boolean = false) { + mVideoEntity?.videoIsMuted = true + volume.setImageResource(R.drawable.ic_article_video_volume_off) + CustomManager.getCustomManager(getKey()).isNeedMute = true + if (isManual) { +// Utils.toast(context, "当前处于静音状态") + uploadVideoStreamingPlaying("点击静音") + } + } + + fun unMute(isManual: Boolean = false) { + mVideoEntity?.videoIsMuted = false + volume.setImageResource(R.drawable.ic_article_video_volume_on) + CustomManager.getCustomManager(getKey()).isNeedMute = false + if (isManual) { + uploadVideoStreamingPlaying("取消静音") + } + } + + override fun onAutoCompletion() { + super.onAutoCompletion() + uploadVideoStreamingPlaying("播放完毕") + } + + override fun onStopTrackingTouch(seekBar: SeekBar?) { + super.onStopTrackingTouch(seekBar) + uploadVideoStreamingPlaying("拖动") + } + + private fun share() { + mVideoEntity?.let { + val shareIcon = it.poster + val shareUrl = if (isPublishEnv()) { + "https://m.ghzs666.com/video/${it.id}" + } else { + "https://resource.ghzs.com/page/video_play/video/video.html?video=${it.id}" + } + ShareUtils.getInstance(context).showShareWindowsCallback(context as Activity, + this, + shareUrl, + shareIcon, + it.title, + it.des, + ShareUtils.ShareEntrance.video, it.id, object : ShareUtils.ShareCallBack { + override fun onSuccess(label: String) { +// if ("短信" == label || "复制链接" == label) viewModel?.shareVideoStatistics(it) + } + + override fun onCancel() { + uploadVideoStreamingPlaying("取消分享") + } + }) + } + } + + fun uploadVideoStreamingPlaying(action: String) { + + } + + fun setFullViewStatus() { + mProgressBar.visibility = View.VISIBLE + mCurrentTimeTextView.visibility = View.VISIBLE + mTotalTimeTextView.visibility = View.VISIBLE + mBottomProgressBar.visibility = View.GONE + remainingTv.visibility = View.GONE + durationTv.visibility = View.GONE + } + + fun getKey(): String { + return uuid + } + + fun getCurrentPosition(): Long { + return mCurrentPosition + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt new file mode 100644 index 0000000000..209dd4e804 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt @@ -0,0 +1,258 @@ +package com.gh.gamecenter.forum.home + +import android.graphics.Typeface +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AnimationUtils +import android.widget.* +import androidx.core.graphics.ColorUtils +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import com.gh.base.adapter.FragmentAdapter +import com.gh.base.fragment.LazyFragment +import com.gh.common.dialog.TrackableDialog +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentCommunityHomeBinding +import com.gh.gamecenter.databinding.TabItemMainBinding +import com.gh.gamecenter.eventbus.EBTypeChange +import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.CommunityFragment +import com.gh.gamecenter.qa.BbsType +import com.gh.gamecenter.qa.article.edit.ArticleEditActivity +import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity +import com.gh.gamecenter.qa.video.publish.VideoPublishActivity +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import kotlin.math.abs + +class CommunityHomeFragment : LazyFragment() { + + private var mBinding: FragmentCommunityHomeBinding? = null + private var mFragmentList = arrayListOf() + private var mTitleList = arrayListOf("推荐", "论坛", "活动") + private var mTabList = arrayListOf() + + override fun getRealLayoutId(): Int { + return R.layout.fragment_community_home + } + + override fun onRealLayoutInflated(inflatedView: View) { + mBinding = FragmentCommunityHomeBinding.bind(inflatedView) + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + + initViewPager() + } + + override fun initRealView() { + super.initRealView() + mBinding?.run { + searchContainer.setOnClickListener { + requireContext().startActivity(ForumOrUserSearchActivity.getIntent(requireContext(), "", "论坛首页")) + } + + communityEditBtn.setOnClickListener { + showCommunityEditWindow() + } + } + } + + override fun onFragmentResume() { + super.onFragmentResume() + + DisplayUtils.setLightStatusBar(requireActivity(), true) + } + + fun setCurrentItem(index: Int) { + mBinding?.viewPager?.currentItem = index + } + + private fun initViewPager() { + mBinding?.run { + mFragmentList.clear() + val tag = "android:switcher:${viewPager.id}:" + val forumArticleListFragment = childFragmentManager.findFragmentByTag("${tag}0") + ?: ForumArticleListFragment().with(bundleOf(EntranceUtils.KEY_ENTRANCE to "社区", EntranceUtils.KEY_PATH to "推荐")) + mFragmentList.add(forumArticleListFragment) + + val forumFragment = childFragmentManager.findFragmentByTag("${tag}1") + ?: ForumFragment().with(bundleOf(EntranceUtils.KEY_ENTRANCE to "社区")) + mFragmentList.add(forumFragment) + + val activityFragment = childFragmentManager.findFragmentByTag("${tag}2") + ?: ForumActivityFragment().with(bundleOf(EntranceUtils.KEY_ENTRANCE to "活动")) + mFragmentList.add(activityFragment) + + viewPager.run { + offscreenPageLimit = mFragmentList.size + adapter = FragmentAdapter(childFragmentManager, mFragmentList, mTitleList) + viewPager.doOnScroll( + onPageSelected = { position -> + updateTabTextStyle(position, 0F) + }, + onPageScrolled = { position, positionOffset, _ -> + if (position + 1 != mTabList.size) { + mTabList[position].run { + textSize = (DEFAULT_TAB_TEXT_SIZE + ((1 - positionOffset) * 4)).roundTo(1) + setTextColor(ColorUtils.blendARGB(TAB_DEFAULT_COLOR, TAB_SELECTED_COLOR, 1 - positionOffset)) + } + mTabList[position + 1].run { + textSize = (DEFAULT_TAB_TEXT_SIZE + ((positionOffset) * 4)).roundTo(1) + setTextColor(ColorUtils.blendARGB(TAB_DEFAULT_COLOR, TAB_SELECTED_COLOR, positionOffset)) + } + + // 多 tab 切换的时候可能会出现某些 tab 的文字没有回归到原始大小的问题的问题 (positionOffset 不保证连续) + for ((index, tabTv) in mTabList.withIndex()) { + if (abs(index - position) >= 2) { + if (tabTv.textSize != DEFAULT_TAB_TEXT_SIZE) { + tabTv.textSize = DEFAULT_TAB_TEXT_SIZE + tabTv.setTextColor(TAB_DEFAULT_COLOR) + } + } + } + } + + updateTabTextStyle(position, positionOffset) + } + ) + } + + tabLayout.setupWithViewPager(viewPager) + indicatorView.run { + setupWithTabLayout(tabLayout) + setupWithViewPager(viewPager) + setIndicatorWidth(18) + } + for (i in 0 until tabLayout.tabCount) { + val tab = tabLayout.getTabAt(i) ?: continue + val tabTitle = if (tab.text != null) tab.text.toString() else "" + val tabViewBinding = generateTabView(tabTitle) + mTabList.add(tabViewBinding.tabTitle) + tab.customView = tabViewBinding.root + tab.view.setPadding(0, 0, 0, 0) + } + } + } + + private fun updateTabTextStyle(selectedPosition: Int, positionOffset: Float) { + for ((index, titleTv) in mTabList.withIndex()) { + if (index == selectedPosition) { + titleTv.setTextColor(TAB_SELECTED_COLOR) + if (positionOffset == 0F) { + titleTv.setTypeface(null, Typeface.NORMAL) + titleTv.setTypeface(titleTv.typeface, Typeface.BOLD) + } + } else { + titleTv.setTextColor(TAB_DEFAULT_COLOR) + if (positionOffset == 0F) { + titleTv.setTypeface(null, Typeface.NORMAL) + } + } + } + } + + private fun generateTabView(title: String): TabItemMainBinding { + val binding = TabItemMainBinding.inflate(LayoutInflater.from(requireContext())) + binding.tabTitle.run { + text = title + textSize = DEFAULT_TAB_TEXT_SIZE + setTextColor(TAB_DEFAULT_COLOR) + } + binding.invisibleTabTitle.run { + text = title + textSize = DEFAULT_TAB_TEXT_SIZE + } + return binding + } + + private fun showCommunityEditWindow() { + val contentView = LayoutInflater.from(context).inflate(R.layout.community_edit_window, null) + val params = ViewGroup.LayoutParams(resources.displayMetrics.widthPixels, ViewGroup.LayoutParams.WRAP_CONTENT) + val dialog = TrackableDialog( + requireContext(), + R.style.DialogWindowTransparent, + "社区", + UserManager.getInstance().community.name, + null, + "发布-空白", + "发布-返回", + false) + val window = dialog.window + window?.setGravity(Gravity.BOTTOM) + window?.setWindowAnimations(R.style.community_publication_animation) + dialog.setContentView(contentView, params) + dialog.show() + contentView.findViewById(R.id.community_edit_article_container).setOnClickListener { + context?.ifLogin("论坛首页", action = { + checkStoragePermissionBeforeAction { + showRegulationTestDialogIfNeeded { + MtaHelper.onEvent("论坛首页", "发布", "发帖子") + startActivity(ArticleEditActivity.getIntent(requireContext(), null)) + dialog.dismiss() + } + } + }) + } + contentView.findViewById(R.id.community_edit_question_container).setOnClickListener { + context?.ifLogin("论坛首页", action = { + checkStoragePermissionBeforeAction { + showRegulationTestDialogIfNeeded { + MtaHelper.onEvent("论坛首页", "发布", "提问") + startActivity(QuestionEditActivity.getIntent(requireContext())) + dialog.dismiss() + } + } + }) + } + contentView.findViewById(R.id.community_edit_video_container).setOnClickListener { + checkStoragePermissionBeforeAction { + showRegulationTestDialogIfNeeded { + MtaHelper.onEvent("论坛首页", "发布", "发视频") + startActivity(VideoPublishActivity.getIntent(requireContext(), null, "", mEntrance, "论坛首页")) + dialog.dismiss() + } + } + } + contentView.findViewById(R.id.community_edit_close).setOnClickListener { + dialog.dismiss() + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBTypeChange) { + if (status.type == CommunityFragment.EB_SHOW_QUESTION_BUTTON) { + setPutQuestionButtonStatus(View.VISIBLE) + } else if (status.type == CommunityFragment.EB_HIDE_QUESTION_BUTTON) { + setPutQuestionButtonStatus(View.GONE) + } + } + + private fun setPutQuestionButtonStatus(visibility: Int) { + mBinding?.run { + if (communityEditBtn.visibility == visibility) return + if (visibility == View.GONE) { + val animation = AnimationUtils.loadAnimation(context, R.anim.button_anim_exit) + communityEditBtn.startAnimation(animation) + } else { + val animation = AnimationUtils.loadAnimation(context, R.anim.button_anim_enter) + communityEditBtn.startAnimation(animation) + } + communityEditBtn.visibility = visibility + } + } + + companion object { + var TAB_SELECTED_COLOR: Int = R.color.text_333333.toColor() + var TAB_DEFAULT_COLOR: Int = R.color.text_666666.toColor() + var DEFAULT_TAB_TEXT_SIZE = 16F + const val TAB_RECOMMEND_INDEX = 0 + const val TAB_FORUM_INDEX = 1 + const val TAB_ACTIVITY_INDEX = 2 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityAdapter.kt new file mode 100644 index 0000000000..8769eac174 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityAdapter.kt @@ -0,0 +1,113 @@ +package com.gh.gamecenter.forum.home + +import android.content.Context +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.constant.ItemViewType +import com.gh.common.exposure.time.TimeUtil +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.ForumActivityItemBinding +import com.gh.gamecenter.entity.ForumActivityEntity + +class ForumActivityAdapter(context: Context, + val mViewModel: ForumActivityViewModel, + val mEntrance: String) : ListAdapter(context) { + + override fun getItemViewType(position: Int): Int { + if (position == itemCount - 1) return ItemViewType.ITEM_FOOTER + return ItemViewType.ITEM_BODY + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.ITEM_FOOTER -> { + val view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false) + FooterViewHolder(view) + } + + else -> { + ForumActivityViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.forum_activity_item, parent, false)) + } + } + } + + override fun getItemCount(): Int { + return if (mEntityList.isNotEmpty()) mEntityList.size + FOOTER_ITEM_COUNT else 0 + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is ForumActivityViewHolder -> { + holder.binding.run { + val activityEntity = mEntityList[position] + entity = activityEntity + executePendingBindings() + + line.goneIf(position == 0) + + var status = "" + val currentTime = TimeUtil.currentTime() + when { + // 进行中 + currentTime > activityEntity.effectTime.start + && (currentTime < activityEntity.effectTime.end + || activityEntity.effectTime.end == null + || activityEntity.effectTime.end == 0L) -> { + status = "进行中" + statusTv.text = "进行中" + statusTv.setTextColor(R.color.theme_font.toColor()) + statusTv.setBackgroundResource(R.drawable.bg_forum_activity_status_blue) + if (activityEntity.effectTime.end == null || activityEntity.effectTime.end == 0L) { + timeTv.text = "长期有效" + } else { + val startTime = TimeUtils.getFormatTime(activityEntity.effectTime.start, "yyyy.MM.dd") + val endTime = TimeUtils.getFormatTime(activityEntity.effectTime.end, "yyyy.MM.dd") + timeTv.text = "$startTime 至 $endTime" + } + } + + // 奖励发放中 + currentTime > activityEntity.awardTime.start + && currentTime < activityEntity.awardTime.end -> { + status = "发放中" + statusTv.text = "发放中" + statusTv.setTextColor(R.color.theme_font.toColor()) + statusTv.setBackgroundResource(R.drawable.bg_forum_activity_status_blue) + val startTime = TimeUtils.getFormatTime(activityEntity.awardTime.start, "yyyy.MM.dd") + val endTime = TimeUtils.getFormatTime(activityEntity.awardTime.end, "yyyy.MM.dd") + timeTv.text = "$startTime 至 $endTime" + } + + // 已结束 + else -> { + status = "已结束" + statusTv.text = "已结束" + statusTv.setTextColor(R.color.white.toColor()) + statusTv.setBackgroundResource(R.drawable.bg_forum_activity_status_gray) + val startTime = TimeUtils.getFormatTime(activityEntity.effectTime.start, "yyyy.MM.dd") + val endTime = TimeUtils.getFormatTime(activityEntity.effectTime.end, "yyyy.MM.dd") + timeTv.text = "$startTime 至 $endTime" + } + } + + root.setOnClickListener { + if (status == "发放中") ToastUtils.toast("活动奖励发放中~") + if (status == "已结束") ToastUtils.toast("活动已结束~") + DirectUtils.directToActivityDetail(mContext, activityEntity.id, mEntrance) + } + } + } + + is FooterViewHolder -> { + holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + } + } + } + + inner class ForumActivityViewHolder(val binding: ForumActivityItemBinding): BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityFragment.kt new file mode 100644 index 0000000000..1a0d16496b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityFragment.kt @@ -0,0 +1,83 @@ +package com.gh.gamecenter.forum.home + +import android.view.View +import android.view.ViewGroup +import android.widget.CheckedTextView +import com.gh.common.util.dip2px +import com.gh.common.util.observeNonNull +import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.LazyListFragment +import com.gh.gamecenter.databinding.FragmentForumActivityBinding +import com.gh.gamecenter.databinding.LayoutForumActivityCategoryItemBinding +import com.gh.gamecenter.entity.ForumActivityCategoryEntity +import com.gh.gamecenter.entity.ForumActivityEntity +import com.google.android.flexbox.FlexboxLayout + +class ForumActivityFragment: LazyListFragment() { + + private var mBinding: FragmentForumActivityBinding? = null + private var mAdapter: ForumActivityAdapter? = null + + override fun getRealLayoutId() = R.layout.fragment_forum_activity + + override fun provideListAdapter() = + mAdapter ?: ForumActivityAdapter(requireContext(), mListViewModel, "论坛-活动").apply { mAdapter = this } + + override fun provideListViewModel(): ForumActivityViewModel { + return viewModelProvider() + } + + override fun onRealLayoutInflated(inflatedView: View) { + mBinding = FragmentForumActivityBinding.bind(inflatedView) + } + + override fun initRealView() { + super.initRealView() + + mListViewModel.categories.observeNonNull(viewLifecycleOwner) { + initCategoryView(ArrayList(it)) + } + } + + override fun getItemDecoration() = null + + private fun initCategoryView(list: ArrayList) { + if (list.isEmpty()) return + + mBinding?.categoryContainer?.visibility = View.VISIBLE + + list.add(0, ForumActivityCategoryEntity(id = "all", name = "全部")) + list.forEachIndexed { index, entity -> + val binding = LayoutForumActivityCategoryItemBinding.inflate(layoutInflater).apply { + val params = FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + params.setMargins(0, 8F.dip2px(), 8F.dip2px(), 0) + params.height = 24F.dip2px() + root.layoutParams = params + + name.text = entity.name + name.isChecked = index == 0 + root.setOnClickListener { + updateCategory(index, entity) + } + } + + mBinding?.categoryContainer?.addView(binding.root) + } + } + + private fun updateCategory(index: Int, entity: ForumActivityCategoryEntity) { + updateCategoryView(index) + mListViewModel.categoryId = entity.id + onRefresh() + } + + private fun updateCategoryView(index: Int) { + mBinding?.categoryContainer?.run { + for (i in 0 until childCount) { + val categoryView = getChildAt(i) as CheckedTextView + categoryView.isChecked = index == i + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityViewModel.kt new file mode 100644 index 0000000000..dc5fb1af9e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumActivityViewModel.kt @@ -0,0 +1,48 @@ +package com.gh.gamecenter.forum.home + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.MutableLiveData +import com.gh.common.util.UrlFilterUtils +import com.gh.common.util.singleToMain +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.entity.ForumActivityCategoryEntity +import com.gh.gamecenter.entity.ForumActivityEntity +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.RetrofitManager +import io.reactivex.Single + +class ForumActivityViewModel(application: Application) : ListViewModel(application) { + + var categoryId = "all" + var categories = MutableLiveData>() + + init { + getActivityCategories() + } + + @SuppressLint("CheckResult") + fun getActivityCategories() { + RetrofitManager.getInstance(getApplication()) + .api + .forumActivityCategories + .compose(singleToMain()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + categories.postValue(data) + } + }) + } + + override fun provideDataObservable(page: Int) = null + + override fun provideDataSingle(page: Int): Single> { + return RetrofitManager.getInstance(getApplication()) + .api.getForumActivities(UrlFilterUtils.getFilterQuery("category_id", categoryId), page) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt index 9397b83b88..958cdac5a1 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt @@ -1,27 +1,29 @@ package com.gh.gamecenter.forum.home +import android.app.Activity import android.text.SpannableStringBuilder -import android.text.Spanned import android.view.View import androidx.core.content.ContextCompat -import androidx.core.text.set import com.gh.base.BaseActivity import com.gh.common.util.* -import com.gh.common.view.CenterImageSpan import com.gh.gamecenter.R import com.gh.gamecenter.databinding.CommunityAnswerItemBinding import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.ForumVideoEntity import com.gh.gamecenter.forum.detail.ForumDetailActivity import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.answer.BaseAnswerOrArticleItemViewHolder -import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.ArticleEntity import com.gh.gamecenter.qa.entity.QuestionsDetailEntity -import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack +import com.shuyu.gsyvideoplayer.utils.OrientationUtils class ForumArticleAskItemViewHolder(val binding: CommunityAnswerItemBinding) : BaseAnswerOrArticleItemViewHolder(binding.root) { @@ -48,6 +50,11 @@ class ForumArticleAskItemViewHolder(val binding: CommunityAnswerItemBinding) : B binding.title.text = SpanBuilder(title).image(0, 1, R.drawable.ic_ask_label).build() } else { binding.content.visibility = View.VISIBLE + if (entity.type == "video") { + binding.content.goneIf(entity.des.isEmpty()) + } else { + binding.content.visibility = View.VISIBLE + } //若文章内有图片和视频,标题后增加‘有视频’标签 issues-1052 if (entity.getPassVideos().isNotEmpty() && entity.images.isNotEmpty()) { val videoSpan = SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build() @@ -60,6 +67,12 @@ class ForumArticleAskItemViewHolder(val binding: CommunityAnswerItemBinding) : B } binding.imageContainer.bindData(entity, entrance, path) + if (entity.type == "video") { + bindVideoData(entity.transformForumVideoEntity()) + } else { + bindArticleVideoData(entity) + } + val user = entity.user binding.userBadgeName.setOnClickListener { binding.userBadgeIcon.performClick() } binding.userBadgeIcon.setOnClickListener { @@ -78,11 +91,149 @@ class ForumArticleAskItemViewHolder(val binding: CommunityAnswerItemBinding) : B MtaHelper.onEvent(getEventId(BaseActivity.mergeEntranceAndPath(entrance, path)), getKey(BaseActivity.mergeEntranceAndPath(entrance, path)), "用户名字") DirectUtils.directToHomeActivity(binding.root.context, entity.user.id, 1, entrance, path) } + binding.concernBtn.setOnClickListener { + debounceActionWithInterval(R.id.concernBtn, 1000) { + CheckLoginUtils.checkLogin(itemView.context, entrance) { + followUser(entity, object : EmptyCallback { + override fun onCallback() { + entity.me.isFollower = true + binding.concernBtn.visibility = View.GONE + binding.followedUserTv.visibility = View.VISIBLE + } + }) + } + } + } binding.executePendingBindings() } + private fun bindVideoData(entity: ForumVideoEntity) { + binding.run { + if (entity.url.isEmpty()) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.GONE + } else { + val videoInfo = entity.videoInfo + val visibleView = if (videoInfo.height > videoInfo.width) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.VISIBLE + verticalVideoView + } else { + horizontalVideoView.visibility = View.VISIBLE + verticalVideoView.visibility = View.GONE + horizontalVideoView + } + + val orientationUtils = OrientationUtils(itemView.context as Activity, visibleView) + orientationUtils.isEnable = false + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(entity.url) + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .setEnlargeImageRes(R.drawable.ic_game_detail_enter_full_screen) + .setShrinkImageRes(R.drawable.ic_game_detail_exit_full_screen) + .setVideoAllCallBack(object : GSYSampleCallBack() { + override fun onQuitFullscreen(url: String?, vararg objects: Any) { + orientationUtils.backToProtVideo() + visibleView.uploadVideoStreamingPlaying("退出全屏") + } + }) + .build(visibleView) + visibleView.run { + updateVideoData(entity) + updateThumb(entity.poster) + updateDurationTv(TimeUtils.formatDuration(entity.length)) + + fullscreenButton.setOnClickListener { + val horizontalVideoView = startWindowFullscreen(itemView.context, true, true) as? ArticleItemVideoView + if (horizontalVideoView == null) { + toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤") + return@setOnClickListener + } + orientationUtils.resolveByClick() + horizontalVideoView.uuid = uuid + horizontalVideoView.updateVideoData(entity) + horizontalVideoView.updateThumb(entity.poster) + horizontalVideoView.violenceUpdateMuteStatus() + horizontalVideoView.setFullViewStatus() + uploadVideoStreamingPlaying("开始播放") + uploadVideoStreamingPlaying("点击全屏") + } + } + } + } + } + + private fun bindArticleVideoData(entity: AnswerEntity) { + binding.run { + if (entity.getPassVideos().isNullOrEmpty()) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.GONE + } else { + val video = entity.getPassVideos()[0] + val visibleView = if (video.height > video.width) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.VISIBLE + verticalVideoView + } else { + horizontalVideoView.visibility = View.VISIBLE + verticalVideoView.visibility = View.GONE + horizontalVideoView + } + + val orientationUtils = OrientationUtils(itemView.context as Activity, visibleView) + orientationUtils.isEnable = false + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(video.url) + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .setEnlargeImageRes(R.drawable.ic_game_detail_enter_full_screen) + .setShrinkImageRes(R.drawable.ic_game_detail_exit_full_screen) + .setVideoAllCallBack(object : GSYSampleCallBack() { + override fun onQuitFullscreen(url: String?, vararg objects: Any) { + orientationUtils.backToProtVideo() + visibleView.uploadVideoStreamingPlaying("退出全屏") + } + }) + .build(visibleView) + visibleView.run { + val forumVideoEntity = entity.transformForumVideoEntity() + updateVideoData(forumVideoEntity) + updateThumb(video.poster) + updateDurationTv(video.duration) + + fullscreenButton.setOnClickListener { + val horizontalVideoView = startWindowFullscreen(itemView.context, true, true) as? ArticleItemVideoView + if (horizontalVideoView == null) { + toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤") + return@setOnClickListener + } + orientationUtils.resolveByClick() + horizontalVideoView.uuid = uuid + horizontalVideoView.updateVideoData(forumVideoEntity) + horizontalVideoView.updateThumb(video.poster) + horizontalVideoView.violenceUpdateMuteStatus() + horizontalVideoView.setFullViewStatus() + uploadVideoStreamingPlaying("开始播放") + uploadVideoStreamingPlaying("点击全屏") + } + } + } + } + } + override fun bindCommendAndVote(entity: AnswerEntity, entrance: String) { - if (entity.type == "community_article") { + if (entity.type == "community_article" || entity.type == "video") { binNormalView(entity) } else { if (entity.questions.answerCount > 0) { @@ -95,12 +246,23 @@ class ForumArticleAskItemViewHolder(val binding: CommunityAnswerItemBinding) : B voteCount.text = "邀请回答" voteIcon.setImageDrawable(ContextCompat.getDrawable(itemView.context, R.drawable.community_invite_follow)) } + + if (entity.bbs.type == "official_bbs") { + forumIcon?.displayGameIcon(entity.bbs.icon, null) + } else { + forumIcon?.displayGameIcon(entity.bbs.game?.getIcon(), entity.bbs.game?.iconSubscript) + } + forumNameTv.setOnClickListener { MtaHelper.onEvent(getEventId(entrance), getKey(entrance), if (entity.bbs.name.isEmpty()) entity.bbs.name else entity.bbs.name) itemView.context.startActivity(ForumDetailActivity.getIntent(itemView.context, entity.bbs.id, entrance)) LogUtils.uploadAccessToBbs(entity.bbs.id, "文章外所属论坛") } + forumNameContainer?.setOnClickListener { + forumNameTv.performClick() + } + commentCountContainer.setOnClickListener { if (filterIllegalCommentStatus(entity.commentable, entity.active)) return@setOnClickListener if (entity.type == "community_article") { @@ -111,14 +273,14 @@ class ForumArticleAskItemViewHolder(val binding: CommunityAnswerItemBinding) : B entrance, "") itemView.context.startActivity(intent) MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "评论图标") + } else if (entity.type == "video") { + itemView.context.startActivity(ForumVideoDetailActivity.getIntent(itemView.context, entity.id + ?: "")) } else { - val intent = if (entity.questions.answerCount > 0) { - QuestionsDetailActivity.getIntent(it.context, entity.questions.id, entrance, "") - } else { + if (entity.questions.answerCount == 0) { MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "我来回答") - AnswerEditActivity.getIntent(it.context, entity.questions, entity.communityName) } - itemView.context.startActivity(intent) + itemView.context.startActivity(NewQuestionDetailActivity.getCommentIntent(it.context, entity.questions.id, entrance, "")) } } @@ -127,7 +289,7 @@ class ForumArticleAskItemViewHolder(val binding: CommunityAnswerItemBinding) : B debounceActionWithInterval(R.id.container_like, 1000) { CheckLoginUtils.checkLogin(itemView.context, entrance) { - if (entity.type == "community_article") { + if (entity.type == "community_article" || entity.type == "video") { MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "点赞图标") if (!voteIcon.isChecked) voteAnswer(entity) else cancelAnswerVote(entity) diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListAdapter.kt index 4e5fb646db..e3ccfba756 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListAdapter.kt @@ -1,8 +1,10 @@ package com.gh.gamecenter.forum.home import android.content.Context +import android.graphics.Color import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.gh.base.BaseActivity @@ -10,18 +12,19 @@ import com.gh.common.constant.ItemViewType import com.gh.common.syncpage.ISyncAdapterHandler import com.gh.common.util.MtaHelper import com.gh.common.util.dip2px +import com.gh.common.util.goneIf import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.CommunityAnswerItemBinding import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.forum.detail.ForumDetailActivity -import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.ArticleEntity -import com.gh.gamecenter.qa.entity.Questions +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity class ForumArticleListAdapter(context: Context, val mEntrance: String, val path: String) : ListAdapter(context), ISyncAdapterHandler { + override fun areItemsTheSame(oldItem: ArticleEntity?, newItem: ArticleEntity?): Boolean { return oldItem?.id == newItem?.id } @@ -38,6 +41,7 @@ class ForumArticleListAdapter(context: Context, val mEntrance: String, val path: view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false) FooterViewHolder(view) } + else -> { view = mLayoutInflater.inflate(R.layout.community_answer_item, parent, false) ForumArticleAskItemViewHolder(CommunityAnswerItemBinding.bind(view)) @@ -55,12 +59,57 @@ class ForumArticleListAdapter(context: Context, val mEntrance: String, val path: val viewHolder = holder as ForumArticleAskItemViewHolder val articleEntity = mEntityList[position] articleEntity.community = CommunityEntity(articleEntity.bbs.id, articleEntity.bbs.name) + + if (articleEntity.type == "bbs_article") articleEntity.type = "community_article" + if (articleEntity.type == "bbs_question") articleEntity.type = "question" + if (articleEntity.type == "bbs_video") articleEntity.type = "video" + + viewHolder.binding.run { + topLine.goneIf(position == 0) + contentContainer.setBackgroundColor(Color.TRANSPARENT) + bottomContainer.setBackgroundColor(Color.TRANSPARENT) + includeVoteAndComment.commentCountContainer.setBackgroundColor(Color.TRANSPARENT) + includeVoteAndComment.voteCountContainer.setBackgroundColor(Color.TRANSPARENT) + } viewHolder.bindForumArticleItem(articleEntity, mEntrance, path) + + if (articleEntity.type == "question") { + if (articleEntity.count.answer > 0) { + viewHolder.commentCount.text = articleEntity.count.answer.toString() + } else { + viewHolder.commentCount.text = "回答" + } + viewHolder.commentCount.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(mContext, R.drawable.community_comment_count), null, null, null) + viewHolder.voteCountContainer.visibility = View.GONE + val params = viewHolder.binding.includeVoteAndComment.root.layoutParams as LinearLayout.LayoutParams + params.width = 80f.dip2px() + viewHolder.binding.includeVoteAndComment.root.layoutParams = params + } else { + viewHolder.voteCountContainer.visibility = View.VISIBLE + val params = viewHolder.binding.includeVoteAndComment.root.layoutParams as LinearLayout.LayoutParams + params.width = 160f.dip2px() + viewHolder.binding.includeVoteAndComment.root.layoutParams = params + } + viewHolder.itemView.setOnClickListener { - MtaHelper.onEvent("论坛首页", viewHolder.getKey(BaseActivity.mergeEntranceAndPath(mEntrance, path)), "${articleEntity.title}(${articleEntity.id})") - mContext.startActivity(ArticleDetailActivity.getIntent(mContext, articleEntity.community, articleEntity.id, "", path)) + when (articleEntity.type) { + "community_article" -> { + MtaHelper.onEvent("论坛首页", viewHolder.getKey(BaseActivity.mergeEntranceAndPath(mEntrance, path)), "${articleEntity.title}(${articleEntity.id})") + mContext.startActivity(ArticleDetailActivity.getIntent(mContext, articleEntity.community, articleEntity.id, "", path)) + } + "video" -> { + MtaHelper.onEvent("论坛首页", viewHolder.getKey(BaseActivity.mergeEntranceAndPath(mEntrance, path)), "${articleEntity.title}(${articleEntity.id})") + mContext.startActivity(ForumVideoDetailActivity.getIntent(mContext, articleEntity.id ?:"")) + } + else -> { + MtaHelper.onEvent("论坛首页", viewHolder.getKey(BaseActivity.mergeEntranceAndPath(mEntrance, path)), "${articleEntity.title}(${articleEntity.id})") + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, articleEntity.id + ?: "", mEntrance, path)) + } + } } } + ItemViewType.ITEM_FOOTER -> { val footerViewHolder = holder as FooterViewHolder footerViewHolder.initItemPadding() diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListFragment.kt index fbac34bc7e..5b8a736ac5 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListFragment.kt @@ -1,103 +1,167 @@ package com.gh.gamecenter.forum.home -import android.os.Bundle import android.view.View -import android.widget.LinearLayout -import android.widget.TextView -import androidx.core.content.ContextCompat -import androidx.core.widget.NestedScrollView import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.gh.common.AppExecutor +import com.gh.common.constant.Constants import com.gh.common.iinterface.IScrollable import com.gh.common.util.* -import com.gh.common.view.DumbRefreshLayout -import com.gh.common.view.divider.HorizontalDividerItemDecoration import com.gh.gamecenter.R -import com.gh.gamecenter.baselist.ListAdapter -import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.baselist.LazyListFragment +import com.gh.gamecenter.databinding.FragmentForumListBinding +import com.gh.gamecenter.entity.ForumEntity import com.gh.gamecenter.eventbus.EBDeleteDetail +import com.gh.gamecenter.eventbus.EBForumRecordChange import com.gh.gamecenter.eventbus.EBTypeChange -import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper.Companion.getPlaySchedule +import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper.Companion.savePlaySchedule import com.gh.gamecenter.qa.CommunityFragment import com.gh.gamecenter.qa.entity.ArticleEntity import com.gh.gamecenter.user.UserViewModel -import kotterknife.bindView +import com.google.android.material.appbar.AppBarLayout import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import kotlin.math.abs -class ForumArticleListFragment : ListFragment(), IScrollable { - - private val mNoLoginView by bindView(R.id.reuse_no_login) - private val mNoFollowView by bindView(R.id.reuse_no_follow) - private val mLoginTv by bindView(R.id.loginTv) +class ForumArticleListFragment : LazyListFragment(), IScrollable { private var mViewModel: ForumArticleListViewModel? = null private var mUserViewModel: UserViewModel? = null - private lateinit var mAdapter: ForumArticleListAdapter + private var mBinding: FragmentForumListBinding? = null + private var mAdapter: ForumArticleListAdapter? = null + private var mScrollCalculatorHelper: ForumScrollCalculatorHelper? = null private var mPath = "" + private var mIsFirst = true + private var mHaveLoadLoginData = false + private var mIsLogin = false - override fun onCreate(savedInstanceState: Bundle?) { + override fun getRealLayoutId() = R.layout.fragment_forum_list + + override fun onFragmentFirstVisible() { mPath = arguments?.getString(EntranceUtils.KEY_PATH) ?: "" - super.onCreate(savedInstanceState) val factory = UserViewModel.Factory(requireActivity().application) mUserViewModel = ViewModelProviders.of(this, factory)[UserViewModel::class.java] mUserViewModel?.loginObsUserinfo?.observe(this, Observer { - if (mPath != "关注") return@Observer - if (it?.data != null) { - mNoLoginView.visibility = View.GONE - onLoadRefresh() - } else { - mNoLoginView.visibility = View.VISIBLE + if (it?.data != null && !mHaveLoadLoginData && !mIsLogin) { + onRefresh() + } + }) + + super.onFragmentFirstVisible() + mScrollCalculatorHelper = ForumScrollCalculatorHelper(R.id.horizontalVideoView, R.id.verticalVideoView, 0) + } + + override fun initRealView() { + super.initRealView() + + mViewModel?.recordForumsLiveData?.observeNonNull(viewLifecycleOwner) { + initRecordForums(it) + } + + mBinding?.run { + appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> + mListRefresh?.isEnabled = abs(verticalOffset) <= 2 + }) + + mListRv.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + if (dy > CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { + EventBus.getDefault().post(EBTypeChange(CommunityFragment.EB_HIDE_QUESTION_BUTTON, 0)) + } else if (dy < -CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { + EventBus.getDefault().post(EBTypeChange(CommunityFragment.EB_SHOW_QUESTION_BUTTON, 0)) + } + } + }) + } + + mListRv.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + mScrollCalculatorHelper?.onScrollStateChanged(mListRv, newState) + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + scroll() } }) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - mListRv.overScrollMode = View.OVER_SCROLL_NEVER - mListRefresh?.isEnabled = false - if (mPath == "关注" && !UserManager.getInstance().isLoggedIn) { - mNoLoginView.visibility = View.VISIBLE + override fun onRealLayoutInflated(inflatedView: View) { + super.onRealLayoutInflated(inflatedView) + mBinding = FragmentForumListBinding.bind(inflatedView) + } + + override fun onFragmentResume() { + super.onFragmentResume() + mIsLogin = CheckLoginUtils.isLogin() + if (mIsFirst) { + mIsFirst = false } else { - mNoLoginView.visibility = View.GONE - onLoadRefresh() + mViewModel?.getForumUnreadStatus() } - mLoginTv.setOnClickListener { - CheckLoginUtils.checkLogin(requireContext(), mEntrance, null) - } - mListRv.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - if (dy > CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { - EventBus.getDefault().post(EBTypeChange(CommunityFragment.EB_HIDE_QUESTION_BUTTON, 0)) - } else if (dy < -CommunityFragment.FOLLOW_HINT_TRIGGER_HEIGHT) { - EventBus.getDefault().post(EBTypeChange(CommunityFragment.EB_SHOW_QUESTION_BUTTON, 0)) - } - } - }) + } + + override fun onResume() { + resumeVideo() + super.onResume() + } + + override fun onPause() { + super.onPause() + pauseVideo() + } + + override fun onDestroy() { + super.onDestroy() + mScrollCalculatorHelper?.currentPlayer?.release() } override fun provideListViewModel(): ForumArticleListViewModel { - mViewModel = viewModelProvider(ForumArticleListViewModel.Factory(mPath)) + mViewModel = viewModelProvider() return mViewModel!! } - override fun getLayoutId(): Int = R.layout.fragment_forum_list + override fun getItemDecoration() = null - override fun getItemDecoration(): RecyclerView.ItemDecoration { - return HorizontalDividerItemDecoration.Builder(requireContext()) - .size(0.5f.dip2px()) - .margin(20f.dip2px()) - .color(ContextCompat.getColor(requireContext(), R.color.text_eeeeee)).build() + override fun provideListAdapter() = mAdapter ?: ForumArticleListAdapter(requireContext(), mEntrance, mPath).apply { mAdapter = this } + + override fun onRefresh() { + super.onRefresh() + mBinding?.recordForumsContainer?.visibility = View.GONE + mBaseHandler.postDelayed({ + tryCatchInRelease { mViewModel?.getRecordForums() } + }, 500) } - override fun provideListAdapter(): ListAdapter<*> { - if (!::mAdapter.isInitialized) { - mAdapter = ForumArticleListAdapter(requireContext(), mEntrance, mPath) + override fun onLoadDone() { + super.onLoadDone() + if (CheckLoginUtils.isLogin()) mHaveLoadLoginData = true + AppExecutor.uiExecutor.executeWithDelay(Runnable { + scroll() + mScrollCalculatorHelper?.onScrollStateChanged(mListRv, RecyclerView.SCROLL_STATE_IDLE) + }, 100) + } + + private fun initRecordForums(list: List) { + mBinding?.run { + recordForumsContainer.visibility = View.VISIBLE + recordForumsMore.setOnClickListener { + (parentFragment as? CommunityHomeFragment)?.run { + setCurrentItem(CommunityHomeFragment.TAB_FORUM_INDEX) + } + } + if (recordForumsRv.adapter != null) { + (recordForumsRv.adapter as? ForumRecordsAdapter)?.setListData(list) + } else { + recordForumsRv.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false) + recordForumsRv.adapter = ForumRecordsAdapter(requireContext(), "社区-论坛-关注论坛", list) + } } - return mAdapter } fun onRefresh(filter: String) { @@ -105,53 +169,92 @@ class ForumArticleListFragment : ListFragment= 0) { + currentPlayer?.onVideoPause() + val position = currentPlayer?.getCurrentPosition() ?: 0L + val video = mViewModel?.videoList?.safelyGetInRelease(currentPosition) + if (video != null) { + savePlaySchedule(MD5Utils.getContentMD5(video.url), position) + } + } + } + } + override fun addSyncPageObserver(): Boolean = true - override fun provideSyncAdapter(): ForumArticleListAdapter = mAdapter + override fun provideSyncAdapter(): ForumArticleListAdapter = provideListAdapter() @Subscribe(threadMode = ThreadMode.MAIN) fun onEvent(detail: EBDeleteDetail){ - val currentEntity = mAdapter.entityList?.find { it.id == detail.id } - val indexOf = mAdapter.entityList?.indexOf(currentEntity) ?: 0 - mAdapter.entityList?.remove(currentEntity) - mAdapter.notifyItemRemoved(indexOf) + mAdapter?.run { + val currentEntity = entityList?.find { it.id == detail.id } + val indexOf = entityList?.indexOf(currentEntity) ?: 0 + entityList?.remove(currentEntity) + notifyItemRemoved(indexOf) + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onForumRecordChange(change: EBForumRecordChange) { + mBinding?.run { + if (recordForumsContainer.visibility == View.VISIBLE) { + mViewModel?.run { + val firstEntity = recordForums.safelyGetInRelease(0) + if (firstEntity?.id == change.forumEntity.id) { + if (firstEntity.unread) { + firstEntity.unread = false + } + return + } + + val findEntity = recordForums.find { it.id == change.forumEntity.id } + if (findEntity != null) { + recordForums.remove(findEntity) + } + recordForums.add(0, change.forumEntity) + } + } else { + mViewModel?.recordForums = arrayListOf(change.forumEntity) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListViewModel.kt index e8ea4d7c9f..d0f000e752 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleListViewModel.kt @@ -1,44 +1,100 @@ package com.gh.gamecenter.forum.home +import android.annotation.SuppressLint import android.app.Application -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.MutableLiveData import com.gh.common.util.UrlFilterUtils +import com.gh.common.util.createRequestBodyAny +import com.gh.common.util.singleToMain import com.gh.gamecenter.baselist.ListViewModel -import com.gh.gamecenter.baselist.LoadStatus -import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.entity.ForumUnreadEntity +import com.gh.gamecenter.entity.ForumVideoEntity import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.entity.ArticleEntity +import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.room.AppDatabase import com.halo.assistant.HaloApp import io.reactivex.Observable -class ForumArticleListViewModel(application: Application, val path: String) : ListViewModel(application) { +class ForumArticleListViewModel(application: Application) : ListViewModel(application) { + private val mForumDao = AppDatabase.getInstance(HaloApp.getInstance()).forumDao() + val recordForumsLiveData = MutableLiveData>() + var recordForums = arrayListOf() + var recordStatusForums = arrayListOf() var sort: String = "time.comment"//排序 time.edit 最新发布 time.comment 最新回复 + var videoList = listOf() - override fun provideDataObservable(page: Int): Observable>? { - return if (path == "关注") { - RetrofitManager.getInstance(getApplication()) - .api.getForumFollowArticle(UserManager.getInstance().userId, UrlFilterUtils.getFilterQuery(sort, "-1"), page) - } else { - RetrofitManager.getInstance(getApplication()) - .api.getForumRecommendsArticle(UrlFilterUtils.getFilterQuery(sort, "-1"), page) - } + init { + setOverLimitSize(0) + getRecordForums() } - override fun load(loadType: LoadType?) { - if (path == "关注" && !UserManager.getInstance().isLoggedIn) return - super.load(loadType) + override fun provideDataObservable(page: Int): Observable>? { + return RetrofitManager.getInstance(getApplication()) + .api.getForumRecommends(UrlFilterUtils.getFilterQuery(sort, "-1"), page) } override fun mergeResultLiveData() { - mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } - } + mResultLiveData.addSource(mListLiveData) { list -> - class Factory(val path: String) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T { - return ForumArticleListViewModel(HaloApp.getInstance().application, path) as T + videoList = list.map { it.transformForumVideoEntity() } + + mResultLiveData.postValue(list) } } + + @SuppressLint("CheckResult") + fun getRecordForums() { + mForumDao.getForum() + .subscribe({ + it?.run { + if (it.isNotEmpty()) { + recordForums = ArrayList(it) + getForumUnreadStatus() + } + } + }, { }) + } + + @SuppressLint("CheckResult") + fun getForumUnreadStatus() { + if (recordForums.isEmpty()) return + + if (!UserManager.getInstance().isLoggedIn) { + recordForumsLiveData.postValue(recordForums) + return + } + + val requestMap = hashMapOf() + recordStatusForums.clear() + recordForums.forEach { + recordStatusForums.add(it.transformUnreadEntity()) + } + requestMap["bbs"] = recordStatusForums + val body = requestMap.createRequestBodyAny() + RetrofitManager.getInstance(getApplication()) + .api + .getForumUnreadStatus(body) + .compose(singleToMain()) + .subscribe(object : BiResponse>() { + + override fun onSuccess(data: List) { + if (data.isNotEmpty() && data.size == recordForums.size) { + data.forEachIndexed { index, unreadEntity -> + recordForums[index].unread = unreadEntity.unread + recordStatusForums[index].unread = unreadEntity.unread + } + } + recordForumsLiveData.postValue(recordForums) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + recordForumsLiveData.postValue(recordForums) + } + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumFragment.kt new file mode 100644 index 0000000000..2961b72fb0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumFragment.kt @@ -0,0 +1,214 @@ +package com.gh.gamecenter.forum.home + +import android.view.View +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.base.fragment.LazyFragment +import com.gh.common.util.observeNonNull +import com.gh.common.util.viewModelProvider +import com.gh.common.view.GridSpacingItemColorDecoration +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentForumBinding +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.eventbus.EBForumFollowChange +import com.gh.gamecenter.eventbus.EBForumRecordChange +import com.gh.gamecenter.forum.list.ForumListActivity +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +class ForumFragment: LazyFragment() { + + private var mBinding: FragmentForumBinding? = null + private var mViewModel: ForumViewModel? = null + private var mHaveFollowForum = false + private var mHaveHotForum = false + private var mIsFirst = true + + override fun getRealLayoutId(): Int { + return R.layout.fragment_forum + } + + override fun onRealLayoutInflated(inflatedView: View) { + mBinding = FragmentForumBinding.bind(inflatedView) + } + + override fun onFragmentFirstVisible() { + mViewModel = viewModelProvider() + super.onFragmentFirstVisible() + } + + override fun initRealView() { + super.initRealView() + + mViewModel?.followForumsLiveData?.observe(viewLifecycleOwner, Observer { + mBinding?.run { + reuseLoading.root.visibility = View.GONE + if (it != null) { + reuseNoConnection.root.visibility = View.GONE + if (it.isNotEmpty()) { + mHaveFollowForum = true + initFollowForums(it) + } + if (!mHaveHotForum) mViewModel?.getHotForum() + } else { + reuseNoneData.root.visibility = View.GONE + reuseNoConnection.root.visibility = View.VISIBLE + reuseNoConnection.root.setOnClickListener { + reuseLoading.root.visibility = View.VISIBLE + mViewModel?.getData() + } + } + } + }) + + mViewModel?.hotForums?.observe(viewLifecycleOwner, Observer { + mBinding?.run { + if (mHaveFollowForum) { + if (it != null && it.isNotEmpty()) { + mHaveHotForum = true + initHotForums(it) + } + } else { + reuseLoading.root.visibility = View.GONE + if (it != null) { + reuseNoConnection.root.visibility = View.GONE + if (it.isNotEmpty()) { + initHotForums(it) + } + } else { + reuseNoneData.root.visibility = View.GONE + reuseNoConnection.root.visibility = View.VISIBLE + reuseNoConnection.root.setOnClickListener { + reuseLoading.root.visibility = View.VISIBLE + mViewModel?.getData() + } + } + } + } + }) + + mViewModel?.officialForums?.observeNonNull(viewLifecycleOwner) { + if (it.isNotEmpty()) initOfficialForums(it) + } + + initWelfare() + } + + override fun onFragmentResume() { + super.onFragmentResume() + + if (mIsFirst) { + mIsFirst = false + } else { + mViewModel?.getForumUnreadStatus() + } + } + + private fun initFollowForums(list: List) { + mBinding?.run { + followForumContainer.visibility = View.VISIBLE + followMore.setOnClickListener { startActivity(ForumListActivity.getIntent(requireContext(), ForumListActivity.TYPE_FOLLOW)) } + if (followForumRv.adapter != null) { + (followForumRv.adapter as? ForumRecordsAdapter)?.setListData(list) + } else { + followForumRv.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false) + followForumRv.adapter = ForumRecordsAdapter(requireContext(), "社区-论坛-关注论坛", ArrayList(list)) + } + } + } + + private fun initHotForums(list: List) { + mBinding?.run { + hotForumContainer.visibility = View.VISIBLE + hotMore.setOnClickListener { startActivity(ForumListActivity.getIntent(requireContext(), ForumListActivity.TYPE_HOT)) } + hotForumRv.layoutManager = LinearLayoutManager(requireContext()) + val newList = if (mHaveFollowForum) { + if (list.size > 5) { + list.subList(0, 5) + } else { + list + } + } else { + if (list.size > 10) { + list.subList(0, 10) + } else { + list + } + } + hotForumRv.adapter = HotForumsAdapter(requireContext(), "社区-论坛-热门论坛", mViewModel, newList) + } + } + + private fun initOfficialForums(list: List) { + mBinding?.run { + officialForumContainer.visibility = View.VISIBLE + officialMore.setOnClickListener { startActivity(ForumListActivity.getIntent(requireContext(), ForumListActivity.TYPE_OFFICIAL)) } + officialForumRv.layoutManager = GridLayoutManager(requireContext(), 2) + val newList = if (list.size > 6) list.subList(0, 6) else list + officialForumRv.adapter = OfficialForumAdapter(requireContext(), "社区-论坛-热门论坛", newList) + officialForumRv.addItemDecoration(GridSpacingItemColorDecoration(requireContext(), 0, 16, R.color.transparent)) + } + } + + private fun initWelfare() { + val welfareLists = arrayListOf>() + welfareLists.run { + add(Pair(R.drawable.ic_forum_tool_box, "工具箱")) + add(Pair(R.drawable.ic_forum_libao_center, "礼包中心")) + add(Pair(R.drawable.ic_forum_game_moment, "游戏动态")) + add(Pair(R.drawable.ic_forum_news, "资讯中心")) + add(Pair(R.drawable.ic_forum_accelerator, "万能加速器")) + } + + mBinding?.run { + otherWelfareContainer.visibility = View.VISIBLE + otherWelfareRv.layoutManager = GridLayoutManager(requireContext(), 2) + otherWelfareRv.adapter = WelfaresAdapter(requireContext(), welfareLists) + otherWelfareRv.addItemDecoration(GridSpacingItemColorDecoration(requireContext(), 0, 16, R.color.transparent)) + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onFollowForumChange(forumFollowChange: EBForumFollowChange) { + mBinding?.run { + if (forumFollowChange.isFollow) { + if (followForumContainer.visibility == View.VISIBLE) { + mViewModel?.run { + followForums.add(0, forumFollowChange.forumEntity) + (followForumRv.adapter as? ForumRecordsAdapter)?.setListData(followForums) + } + } else { + mViewModel?.followForums = arrayListOf(forumFollowChange.forumEntity) + initFollowForums(listOf(forumFollowChange.forumEntity)) + } + } else { + mViewModel?.run { + val findEntity = followForums.find { it.id == forumFollowChange.forumEntity.id } + followForums.remove(findEntity) + if (followForums.isEmpty()) { + followForumContainer.visibility = View.GONE + } else{ + (followForumRv.adapter as? ForumRecordsAdapter)?.setListData(followForums) + } + } + } + + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onForumRecordChange(change: EBForumRecordChange) { + mBinding?.run { + if (followForumContainer.visibility == View.VISIBLE) { + mViewModel?.run { + val findIndex = followForums.indexOfFirst { it.id == change.forumEntity.id } + if (findIndex != -1 && followForums[findIndex].unread) { + followForums[findIndex].unread = false + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumHomeFragment.kt index 6fb9a95271..117fb3026f 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/ForumHomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumHomeFragment.kt @@ -30,15 +30,17 @@ import com.gh.gamecenter.entity.LinkEntity import com.gh.gamecenter.eventbus.EBForumFollowChange import com.gh.gamecenter.eventbus.EBTypeChange import com.gh.gamecenter.eventbus.EBUISwitch -import com.gh.gamecenter.forum.follow.ForumMyFollowActivity +import com.gh.gamecenter.forum.list.ForumListActivity import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity import com.gh.gamecenter.forum.select.ForumSelectActivity import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.CommunityFragment +import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.article.edit.ArticleEditActivity import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity import com.gh.gamecenter.qa.recommends.AskRecommendsSubjectPageAdapter +import com.gh.gamecenter.qa.video.publish.VideoPublishActivity import com.gh.gamecenter.user.UserViewModel import com.google.android.material.appbar.AppBarLayout import kotterknife.bindView @@ -247,7 +249,7 @@ class ForumHomeFragment : BaseLazyTabFragment() { R.id.forumFollowTv -> { CheckLoginUtils.checkLogin(requireContext(), "论坛") { MtaHelper.onEvent("论坛首页", "关注的论坛", "更多论坛") - requireContext().startActivity(ForumMyFollowActivity.getIntent(requireContext())) + requireContext().startActivity(ForumListActivity.getIntent(requireContext(), ForumListActivity.TYPE_FOLLOW)) } } } @@ -330,7 +332,7 @@ class ForumHomeFragment : BaseLazyTabFragment() { window?.setWindowAnimations(R.style.community_publication_animation) dialog.setContentView(contentView, params) dialog.show() - contentView.findViewById(R.id.community_edit_article).setOnClickListener { + contentView.findViewById(R.id.community_edit_article_container).setOnClickListener { context?.ifLogin("论坛首页", action = { checkStoragePermissionBeforeAction { showRegulationTestDialogIfNeeded { @@ -341,7 +343,7 @@ class ForumHomeFragment : BaseLazyTabFragment() { } }) } - contentView.findViewById(R.id.community_edit_question).setOnClickListener { + contentView.findViewById(R.id.community_edit_question_container).setOnClickListener { context?.ifLogin("论坛首页", action = { checkStoragePermissionBeforeAction { showRegulationTestDialogIfNeeded { @@ -352,6 +354,15 @@ class ForumHomeFragment : BaseLazyTabFragment() { } }) } + contentView.findViewById(R.id.community_edit_video_container).setOnClickListener { + checkStoragePermissionBeforeAction { + showRegulationTestDialogIfNeeded { + MtaHelper.onEvent("论坛首页", "发布", "发视频") + startActivity(VideoPublishActivity.getIntent(requireContext(), null, "", mEntrance, "论坛首页")) + dialog.dismiss() + } + } + } contentView.findViewById(R.id.community_edit_close).setOnClickListener { dialog.dismiss() } @@ -398,7 +409,7 @@ class ForumHomeFragment : BaseLazyTabFragment() { @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(busNine: EBUISwitch) { - if (busNine.position == MainWrapperFragment.INDEX_ASK) { + if (busNine.position == MainWrapperFragment.INDEX_BBS) { mFollowForumArticleListFragment?.scrollToTop() mRecommendForumArticleListFragment?.scrollToTop() appbar.setExpanded(true) diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumRecordDao.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumRecordDao.kt new file mode 100644 index 0000000000..35ec1e0875 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumRecordDao.kt @@ -0,0 +1,80 @@ +package com.gh.gamecenter.forum.home + +import com.gh.common.util.SPUtils +import com.gh.gamecenter.entity.ForumEntity + +class ForumRecordDao { + fun add(id: String) { + val originString = SPUtils.getString(SP_KEY) + + if (originString.isEmpty()) { + SPUtils.setString(SP_KEY, id) + } else { + getAll()?.let { + if (it.contains(id)) { + it.remove(id) + } + it.add(0, id) + save(it) + } + } + } + + fun delete(id: String) { + val originString = SPUtils.getString(SP_KEY) + if (originString.isNotEmpty()) { + getAll()?.let { + if (it.contains(id)) { + it.remove(id) + } + save(it) + } + } + } + + private fun save(it: ArrayList) { + val builder = StringBuilder() + for ((index, key) in it.withIndex()) { + builder.append(key) + if (index != it.size - 1) { + builder.append(DIVIDER) + } + } + SPUtils.setString(SP_KEY, builder.toString()) + } + + fun sortForumList(originalForums: ArrayList?) { + if (originalForums.isNullOrEmpty()) return + val visitRecords = getAll() + val tempList = arrayListOf() + visitRecords?.forEach { id -> + val index = originalForums.indexOfFirst { it.id == id } + if (index >= 0) { + tempList.add(originalForums.removeAt(index)) + } + } + tempList.addAll(originalForums) + originalForums.clear() + originalForums.addAll(tempList) + } + + fun getAll(): ArrayList? { + val list = SPUtils.getString(SP_KEY).split(DIVIDER) + + return if (list.size == 1 && list[0].isEmpty()) null else ArrayList(list) + } + + companion object { + private const val SP_KEY = "forum_recoed_key" + private const val DIVIDER = "<-||->" + + @Volatile + private var instance: ForumRecordDao? = null + + fun getInstance(): ForumRecordDao { + return instance ?: synchronized(this) { + instance ?: ForumRecordDao().also { instance = it } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumRecordsAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumRecordsAdapter.kt new file mode 100644 index 0000000000..d033ff6ec3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumRecordsAdapter.kt @@ -0,0 +1,53 @@ +package com.gh.gamecenter.forum.home + +import android.content.Context +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.ForumRecordItemBinding +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.forum.detail.ForumDetailActivity +import com.lightgame.adapter.BaseRecyclerAdapter + +class ForumRecordsAdapter(context: Context, + private val entrance: String, + private var mList: List) + : BaseRecyclerAdapter(context) { + + fun setListData(list: List) { + mList = list + notifyDataSetChanged() + } + + override fun getItemCount() = mList.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) + = ForumRecordViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.forum_record_item, parent, false)) + + + override fun onBindViewHolder(holder: ForumRecordViewHolder, position: Int) { + holder.binding.run { + root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply { + leftMargin = if (position == 0) 16F.dip2px() else 0 + } + + val forumEntity = mList[position] + entity = forumEntity + executePendingBindings() + + if (forumEntity.type == "official_bbs") { + forumIv.displayGameIcon(forumEntity.icon, null) + } else { + forumIv.displayGameIcon(forumEntity.game.getIcon(), forumEntity.game.iconSubscript) + } + + root.setOnClickListener { + mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, entrance)) + } + } + } + + class ForumRecordViewHolder(val binding: ForumRecordItemBinding) : BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumScrollCalculatorHelper.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumScrollCalculatorHelper.kt new file mode 100644 index 0000000000..9554fc834a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumScrollCalculatorHelper.kt @@ -0,0 +1,167 @@ +package com.gh.gamecenter.forum.home + +import android.graphics.Rect +import android.os.Handler +import android.os.Looper +import android.text.TextUtils +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.Constants +import com.gh.common.util.MD5Utils +import com.gh.common.util.NetworkUtils +import com.gh.common.util.SPUtils +import com.gh.common.util.safelyGetInRelease +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.setting.VideoSettingFragment +import com.gh.gamecenter.video.detail.CustomManager +import com.halo.assistant.HaloApp +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer + +class ForumScrollCalculatorHelper(private val horizontalId: Int, + private val verticalId: Int, + private val rangeTop: Int) { + private var firstVisible = -1 + private var lastVisible = 0 + private var visibleCount = 0 + private var runnable: PlayRunnable? = null + var mItemData: List? = null + var currentPlayer: ArticleItemVideoView? = null + var currentPosition = -1 + var mListRv: RecyclerView? = null + + private val playHandler = Handler(Looper.getMainLooper()) + + fun onScrollStateChanged(view: RecyclerView?, scrollState: Int) { + mListRv = view + if (scrollState == RecyclerView.SCROLL_STATE_IDLE) { + playVideo(view) + } + } + + fun onScroll(itemData: List?, firstVisibleItem: Int, lastVisibleItem: Int) { + mItemData = itemData + firstVisible = firstVisibleItem + lastVisible = lastVisibleItem + visibleCount = lastVisibleItem - firstVisibleItem + releaseIfNeeded() + } + + //判断player是否划出了屏幕,划出了屏幕则需要释放 + private fun releaseIfNeeded() { + val rect = Rect() + if (currentPlayer != null) { + currentPlayer?.getLocalVisibleRect(rect) + val height = currentPlayer?.height + if (rect.top != 0 || rect.bottom != height) { + //保存进度 + val currentScheduler = currentPlayer?.currentPositionWhenPlaying?.toLong() ?: 0L + val video = mItemData?.safelyGetInRelease(currentPosition) + if (video != null) { + savePlaySchedule(MD5Utils.getContentMD5(video.url), currentScheduler) + } + CustomManager.releaseAllVideos(currentPlayer?.getKey()) +// currentPlayer?.resetDetailMask() + currentPlayer = null + currentPosition = -1 + } + } + } + + private fun playVideo(view: RecyclerView?) { + if (view == null) return + val layoutManager = view.layoutManager + var gsyBaseVideoPlayer: ArticleItemVideoView + for (i in firstVisible until lastVisible + 1) { + if (layoutManager == null || mItemData.isNullOrEmpty()) return + + val child = mListRv?.findViewHolderForAdapterPosition(i)?.itemView + val horizontalPlayer: View? = child?.findViewById(horizontalId) + val verticalPlayer: View? = child?.findViewById(verticalId) + val player = when { + horizontalPlayer?.visibility == View.VISIBLE -> horizontalPlayer + verticalPlayer?.visibility == View.VISIBLE -> verticalPlayer + else -> null + } + if (player == null || player !is ArticleItemVideoView) continue + + val video = mItemData?.safelyGetInRelease(i) + if (video == null || TextUtils.isEmpty(video.url)) continue + + val rect = Rect() + player.getLocalVisibleRect(rect) + val height = player.height + if (rect.top == 0 && rect.bottom == height) { + gsyBaseVideoPlayer = player + if (runnable != null) { + playHandler.removeCallbacks(runnable!!) + runnable = null + } + if (currentPlayer == gsyBaseVideoPlayer) return + val screenPosition = IntArray(2) + gsyBaseVideoPlayer.getLocationInWindow(screenPosition) + val rangePosition = screenPosition[1] + if (rangePosition >= rangeTop) { + runnable = PlayRunnable(gsyBaseVideoPlayer) + if (currentPlayer != null) { + CustomManager.releaseAllVideos(currentPlayer?.getKey()) +// currentPlayer?.resetDetailMask() + } + currentPlayer = gsyBaseVideoPlayer + currentPosition = i + //降低频率 + playHandler.postDelayed(runnable!!, 100) + break + } + } + + } + } + + private inner class PlayRunnable(var gsyBaseVideoPlayer: GSYBaseVideoPlayer?) : Runnable { + override fun run() { + if (gsyBaseVideoPlayer != null && !gsyBaseVideoPlayer!!.isInPlayingState) { + + val videoOption = SPUtils.getString(Constants.SP_CONTENT_VIDEO_OPTION, VideoSettingFragment.VIDEO_OPTION_WIFI) + ?: VideoSettingFragment.VIDEO_OPTION_WIFI + + when (videoOption) { + VideoSettingFragment.VIDEO_OPTION_ALL -> { + startPlayLogic(gsyBaseVideoPlayer) + } + + VideoSettingFragment.VIDEO_OPTION_WIFI -> { + if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) { + startPlayLogic(gsyBaseVideoPlayer) + } + } + + else -> {} + } + } + } + } + + private fun startPlayLogic(gsyBaseVideoPlayer: GSYBaseVideoPlayer?) { + val videoView = gsyBaseVideoPlayer as? ArticleItemVideoView +// val position = getPlaySchedule(MD5Utils.getContentMD5(topVideo.url)) +// if (position > 0) { +// videoView?.seekOnStart = position +// } + videoView?.startPlayLogic() + } + + companion object { + + fun savePlaySchedule(key: String, schedule: Long) { + val record = SPUtils.getMap(Constants.SP_HOME_VIDEO_PLAY_RECORD) + record[key] = schedule.toString() + SPUtils.setMap(Constants.SP_CONTENT_VIDEO_PLAY_RECORD, record) + } + + fun getPlaySchedule(key: String): Long { + val record = SPUtils.getMap(Constants.SP_CONTENT_VIDEO_PLAY_RECORD) + return record[key]?.toLong() ?: 0L + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumViewModel.kt new file mode 100644 index 0000000000..ca2198944c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumViewModel.kt @@ -0,0 +1,164 @@ +package com.gh.gamecenter.forum.home + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.* +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.createRequestBodyAny +import com.gh.common.util.singleToMain +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.entity.ForumUnreadEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody +import retrofit2.HttpException + +class ForumViewModel(application: Application) : AndroidViewModel(application) { + + private val api = RetrofitManager.getInstance(getApplication()).api + val followForumsLiveData = MediatorLiveData>() + val hotForums = MediatorLiveData>() + val officialForums = MediatorLiveData>() + var followForums = arrayListOf() + var followStatusForums = arrayListOf() + + init { + getData() + } + + fun getData() { + if (CheckLoginUtils.isLogin()) { + getFollowForum() + } else { + getHotForum() + } + getOfficialForum() + } + + @SuppressLint("CheckResult") + fun getFollowForum() { + api.getFollowsForum(UserManager.getInstance().userId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response>() { + override fun onResponse(response: List?) { + super.onResponse(response) + if (response?.isNotEmpty() == true) { + followForums = ArrayList(response) + getForumUnreadStatus() + } else { + followForumsLiveData.postValue(response) + } + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + followForumsLiveData.postValue(null) + } + }) + } + + @SuppressLint("CheckResult") + fun getForumUnreadStatus() { + if (followForums.isEmpty()) return + + if (!UserManager.getInstance().isLoggedIn) { + followForumsLiveData.postValue(followForums) + return + } + + val requestMap = hashMapOf() + followStatusForums.clear() + followForums.forEach { + followStatusForums.add(it.transformUnreadEntity()) + } + requestMap["bbs"] = followStatusForums + val body = requestMap.createRequestBodyAny() + RetrofitManager.getInstance(getApplication()) + .api + .getForumUnreadStatus(body) + .compose(singleToMain()) + .subscribe(object : BiResponse>() { + + override fun onSuccess(data: List) { + if (data.isNotEmpty() && data.size == followForums.size) { + data.forEachIndexed { index, unreadEntity -> + followForums[index].unread = unreadEntity.unread + } + } + followForumsLiveData.postValue(followForums) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + followForumsLiveData.postValue(followForums) + } + }) + } + + @SuppressLint("CheckResult") + fun getHotForum() { + api.getHotForumWithPage(1) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response>() { + override fun onResponse(response: List?) { + super.onResponse(response) + hotForums.postValue(response) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + hotForums.postValue(null) + } + }) + } + + @SuppressLint("CheckResult") + fun getOfficialForum() { + api.getOfficialForum(1) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response>() { + override fun onResponse(response: List?) { + super.onResponse(response) + officialForums.postValue(response) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + officialForums.postValue(null) + } + }) + } + + @SuppressLint("CheckResult") + fun followForum(bbsId: String, onSuccess: () -> Unit) { + RetrofitManager.getInstance(getApplication()).api + .followForum(bbsId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + onSuccess.invoke() + } + }) + } + + @SuppressLint("CheckResult") + fun unFollowForum(bbsId: String, onSuccess: () -> Unit) { + RetrofitManager.getInstance(getApplication()).api + .unFollowForum(bbsId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + onSuccess.invoke() + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/HotForumsAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/HotForumsAdapter.kt new file mode 100644 index 0000000000..45ed048e48 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/HotForumsAdapter.kt @@ -0,0 +1,81 @@ +package com.gh.gamecenter.forum.home + +import android.content.Context +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.HotForumItemBinding +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.eventbus.EBForumFollowChange +import com.gh.gamecenter.forum.detail.ForumDetailActivity +import com.lightgame.adapter.BaseRecyclerAdapter +import org.greenrobot.eventbus.EventBus + +class HotForumsAdapter(context: Context, + private val entrance: String, + private val mViewModel: ForumViewModel?, + private var mList: List) + : BaseRecyclerAdapter(context) { + + + override fun getItemCount() = mList.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) + = HotForumViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.hot_forum_item, parent, false)) + + + override fun onBindViewHolder(holder: HotForumViewHolder, position: Int) { + holder.binding.run { + val forumEntity = mList[position] + entity = forumEntity + executePendingBindings() + + forumIv.displayGameIcon(forumEntity.game.getIcon(), forumEntity.game.iconSubscript) + + followTv.run { + text = if (forumEntity.isFollow) { + setBackgroundResource(R.drawable.button_round_fafafa) + setTextColor(R.color.text_C0C6CC.toColor()) + "已关注" + } else { + setBackgroundResource(R.drawable.button_round_f0f8fa) + setTextColor(R.color.theme_font.toColor()) + "关注" + } + } + + followTv.setOnClickListener { + debounceActionWithInterval(R.id.followTv) { + CheckLoginUtils.checkLogin(mContext, entrance) { + if (forumEntity.isFollow) { + mViewModel?.unFollowForum(forumEntity.id) { + forumEntity.isFollow = false + followTv.setBackgroundResource(R.drawable.button_round_f0f8fa) + followTv.setTextColor(R.color.theme_font.toColor()) + followTv.text = "关注" + EventBus.getDefault().post(EBForumFollowChange(forumEntity, false)) + } + } else { + mViewModel?.followForum(forumEntity.id) { + forumEntity.isFollow = true + followTv.setBackgroundResource(R.drawable.button_round_fafafa) + followTv.setTextColor(R.color.text_C0C6CC.toColor()) + followTv.text = "已关注" + ToastUtils.showToast("关注成功") + EventBus.getDefault().post(EBForumFollowChange(forumEntity, true)) + } + } + } + } + } + + root.setOnClickListener { + mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, entrance)) + } + } + } + + class HotForumViewHolder(val binding: HotForumItemBinding) : BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/OfficialForumAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/OfficialForumAdapter.kt new file mode 100644 index 0000000000..e48ea70c16 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/OfficialForumAdapter.kt @@ -0,0 +1,46 @@ +package com.gh.gamecenter.forum.home + +import android.content.Context +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.MtaHelper +import com.gh.common.util.ifLogin +import com.gh.gamecenter.* +import com.gh.gamecenter.databinding.ForumWelfareItemBinding +import com.gh.gamecenter.databinding.HotForumItemBinding +import com.gh.gamecenter.databinding.OfficialForumItemBinding +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.forum.detail.ForumDetailActivity +import com.gh.gamecenter.manager.UserManager +import com.lightgame.adapter.BaseRecyclerAdapter + +class OfficialForumAdapter(context: Context, + private val entrance: String, + private var mList: List) + : BaseRecyclerAdapter(context) { + + + override fun getItemCount() = mList.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) + = OfficialForumViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.official_forum_item, parent, false)) + + + override fun onBindViewHolder(holder: OfficialForumViewHolder, position: Int) { + holder.binding.run { + + val forumEntity = mList[position] + entity = forumEntity + executePendingBindings() + + root.setOnClickListener { + mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, entrance)) + } + } + } + + class OfficialForumViewHolder(val binding: OfficialForumItemBinding) : BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt new file mode 100644 index 0000000000..b080a9f15b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt @@ -0,0 +1,70 @@ +package com.gh.gamecenter.forum.home + +import android.content.Context +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.MtaHelper +import com.gh.common.util.ifLogin +import com.gh.gamecenter.* +import com.gh.gamecenter.databinding.ForumWelfareItemBinding +import com.gh.gamecenter.databinding.HotForumItemBinding +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.forum.detail.ForumDetailActivity +import com.gh.gamecenter.manager.UserManager +import com.lightgame.adapter.BaseRecyclerAdapter + +class WelfaresAdapter(context: Context, + private var mList: List>) + : BaseRecyclerAdapter(context) { + + + override fun getItemCount() = mList.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) + = WelfareViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.forum_welfare_item, parent, false)) + + + override fun onBindViewHolder(holder: WelfareViewHolder, position: Int) { + holder.binding.run { +// root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply { +// leftMargin = if (position == 0) 16F.dip2px() else 0 +// } + + val entity = mList[position] + welfareIv.setImageResource(entity.first) + welfareName.text = entity.second + + + root.setOnClickListener { + when (entity.second) { + "工具箱" -> { + mContext.startActivity(ToolBoxActivity.getIntent(mContext, "(社区-论坛:工具箱)")) + } + + "礼包中心" -> { + mContext.startActivity(LibaoActivity.getIntent(mContext, "(社区-论坛:礼包中心)")) + } + + "游戏动态" -> { + CheckLoginUtils.checkLogin(mContext, "社区-论坛:游戏动态") { + mContext.startActivity(ConcernInfoActivity.getIntent(mContext)) + } + } + + "资讯中心" -> { + mContext.startActivity(InfoActivity.getIntent(mContext)) + } + + "万能加速器" -> { + GameDetailActivity.startGameDetailActivity(mContext, "5c9456576b90b400137cc6a6", "社区-论坛:万能加速器") + } + } + } + } + } + + class WelfareViewHolder(val binding: ForumWelfareItemBinding) : BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/list/ForumListActivity.kt b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListActivity.kt new file mode 100644 index 0000000000..bb19f73862 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListActivity.kt @@ -0,0 +1,22 @@ +package com.gh.gamecenter.forum.list + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity + +class ForumListActivity : NormalActivity() { + + companion object { + const val TYPE_FOLLOW = "follow" + const val TYPE_HOT = "hot" + const val TYPE_OFFICIAL = "official" + + fun getIntent(context: Context, type: String): Intent { + val bundle = Bundle() + bundle.putString(EntranceUtils.KEY_TYPE, type) + return getTargetIntent(context, ForumListActivity::class.java, ForumListFragment::class.java, bundle) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/list/ForumListAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListAdapter.kt new file mode 100644 index 0000000000..1a00d7d358 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListAdapter.kt @@ -0,0 +1,117 @@ +package com.gh.gamecenter.forum.list + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.constant.ItemViewType +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.ToastUtils +import com.gh.common.util.debounceActionWithInterval +import com.gh.common.util.toColor +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.ForumMyFollowBinding +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.eventbus.EBForumFollowChange +import com.gh.gamecenter.forum.detail.ForumDetailActivity +import org.greenrobot.eventbus.EventBus + +class ForumListAdapter(context: Context, + val entrance: String, + val mViewModel: ForumListViewModel?): ListAdapter(context) { + + override fun getItemCount(): Int { + return if (mEntityList.isNullOrEmpty()) 0 else mEntityList.size + FOOTER_ITEM_COUNT + } + + override fun getItemViewType(position: Int): Int { + return when (position) { + itemCount - 1 -> ItemViewType.ITEM_FOOTER + else -> ItemViewType.ITEM_BODY + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.ITEM_FOOTER -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) + + else -> ForumItemViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.forum_my_follow, parent, false)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is ForumItemViewHolder -> { + holder.binding.run { + val forumEntity = mEntityList[position] + entity = forumEntity + executePendingBindings() + + if (mViewModel?.type == ForumListActivity.TYPE_OFFICIAL) { + forumIcon.displayGameIcon(forumEntity.icon, null) + } else { + forumIcon.displayGameIcon(forumEntity.game.getIcon(), forumEntity.game.iconSubscript) + } + topLine.visibility = if (position == 0) View.GONE else View.VISIBLE + + if (mViewModel?.type == ForumListActivity.TYPE_FOLLOW) { + moreIv.visibility = View.VISIBLE + followTv.visibility = View.GONE + } else { + moreIv.visibility = View.GONE + followTv.visibility = View.VISIBLE + + followTv.run { + text = if (forumEntity.isFollow) { + setBackgroundResource(R.drawable.button_round_fafafa) + setTextColor(R.color.text_C0C6CC.toColor()) + "已关注" + } else { + setBackgroundResource(R.drawable.button_round_f0f8fa) + setTextColor(R.color.theme_font.toColor()) + "关注" + } + } + + followTv.setOnClickListener { + debounceActionWithInterval(R.id.followTv) { + CheckLoginUtils.checkLogin(mContext, entrance) { + if (forumEntity.isFollow) { + mViewModel?.unFollowForum(forumEntity.id) { + forumEntity.isFollow = false + followTv.setBackgroundResource(R.drawable.button_round_f0f8fa) + followTv.setTextColor(R.color.theme_font.toColor()) + followTv.text = "关注" + EventBus.getDefault().post(EBForumFollowChange(forumEntity, false)) + } + } else { + mViewModel?.followForum(forumEntity.id) { + forumEntity.isFollow = true + followTv.setBackgroundResource(R.drawable.button_round_fafafa) + followTv.setTextColor(R.color.text_C0C6CC.toColor()) + followTv.text = "已关注" + ToastUtils.showToast("关注成功") + EventBus.getDefault().post(EBForumFollowChange(forumEntity, true)) + } + } + } + } + } + } + + root.setOnClickListener { mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, entrance)) } + } + } + + is FooterViewHolder -> { + holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + } + } + } + + inner class ForumItemViewHolder(val binding: ForumMyFollowBinding): BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/list/ForumListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListFragment.kt new file mode 100644 index 0000000000..c989ff5952 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListFragment.kt @@ -0,0 +1,41 @@ +package com.gh.gamecenter.forum.list + +import android.os.Bundle +import com.gh.common.util.EntranceUtils +import com.gh.common.util.dip2px +import com.gh.common.util.toColor +import com.gh.common.util.viewModelProvider +import com.gh.common.view.SpacingItemDecoration +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.entity.ForumEntity + +class ForumListFragment: ListFragment() { + + private var mAdapter: ForumListAdapter? = null + private var mViewModel: ForumListViewModel? = null + + + override fun provideListAdapter() = mAdapter + ?: ForumListAdapter(requireContext(), mEntrance, provideListViewModel()).apply { mAdapter = this } + + override fun provideListViewModel() = viewModelProvider() + + override fun getItemDecoration()= SpacingItemDecoration(onlyDecorateTheFirstItem = true, top = 8F.dip2px()) + + override fun onCreate(savedInstanceState: Bundle?) { + val type = arguments?.getString(EntranceUtils.KEY_TYPE) ?: "" + mViewModel = provideListViewModel() + mViewModel?.type = type + when (type) { + ForumListActivity.TYPE_FOLLOW -> { + setNavigationTitle("关注论坛") + mViewModel?.setOverLimitSize(1000) + } + ForumListActivity.TYPE_HOT -> setNavigationTitle("热门论坛") + ForumListActivity.TYPE_OFFICIAL -> setNavigationTitle("综合论坛") + } + + super.onCreate(savedInstanceState) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/list/ForumListVIewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListVIewModel.kt new file mode 100644 index 0000000000..fce7c1e799 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/list/ForumListVIewModel.kt @@ -0,0 +1,57 @@ +package com.gh.gamecenter.forum.list + +import android.annotation.SuppressLint +import android.app.Application +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.RetrofitManager +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody + +class ForumListViewModel(application: Application): ListViewModel(application) { + + private val mApi = RetrofitManager.getInstance(getApplication()).api + var type = ForumListActivity.TYPE_FOLLOW + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } + } + + override fun provideDataObservable(page: Int): Observable> { + return when (type) { + ForumListActivity.TYPE_FOLLOW -> mApi.getFollowsForum(UserManager.getInstance().userId) + ForumListActivity.TYPE_HOT -> mApi.getHotForumWithPage(page) + ForumListActivity.TYPE_OFFICIAL -> mApi.getOfficialForum(page) + else -> mApi.getHotForumWithPage(page) + } + } + + @SuppressLint("CheckResult") + fun followForum(bbsId: String, onSuccess: () -> Unit) { + mApi.followForum(bbsId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + onSuccess.invoke() + } + }) + } + + @SuppressLint("CheckResult") + fun unFollowForum(bbsId: String, onSuccess: () -> Unit) { + mApi.unFollowForum(bbsId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + onSuccess.invoke() + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt index 1f236f8bbd..cace5649a3 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt @@ -20,12 +20,12 @@ import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.CommunityAnswerItemBinding import com.gh.gamecenter.databinding.ForumSearchContentListBinding import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.entity.RatingComment import com.gh.gamecenter.forum.home.ForumArticleAskItemViewHolder import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.Questions -import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity class ForumContentSearchListAdapter(context: Context, val mListViewModel: ForumContentSearchListViewModel, val mEntrance: String) : ListAdapter(context), ISyncAdapterHandler { @@ -73,7 +73,7 @@ class ForumContentSearchListAdapter(context: Context, val mListViewModel: ForumC val questions = Questions() questions.id = answer.id ?: "" questions.title = answer.articleTitle - questions.answerCount = answer.answerCount + questions.answerCount = answer.count.answer answer.questions = questions if (mEntrance == "论坛首页+(搜索)") { @@ -101,61 +101,93 @@ class ForumContentSearchListAdapter(context: Context, val mListViewModel: ForumC answerViewHolder.binding.content.text = answer.brief?.fromHtml() answerViewHolder.itemView.setOnClickListener { val entrance = BaseActivity.mergeEntranceAndPath(mEntrance, "") - if ("community_article" == answer.type) { - MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") - mContext.startActivity(ArticleDetailActivity.getIntent(mContext, CommunityEntity(answer.bbs.id), answer.id!!, mEntrance, "")) - } else { - MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, answer.id, mEntrance, "")) + when (answer.type) { + "community_article" -> { + MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") + mContext.startActivity(ArticleDetailActivity.getIntent(mContext, CommunityEntity(answer.bbs.id), answer.id!!, mEntrance, "")) + } + "video" -> { + MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") + mContext.startActivity(ForumVideoDetailActivity.getIntent(mContext, answer.id ?:"")) + } + else -> { + MtaHelper.onEvent(holder.getEventId(entrance), holder.getKey(entrance), "${answer.articleTitle}(${answer.id})") + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, answer.id?:"", mEntrance, "")) + } } } - if (answer.type != "community_article") { + if (answer.type == "question") { if (answer.questions.answerCount > 0) { answerViewHolder.commentCount.text = answer.questions.answerCount.toString() } else { answerViewHolder.commentCount.text = "回答" } + answerViewHolder.commentCount.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(mContext, R.drawable.community_comment_count), null, null, null) answerViewHolder.voteCountContainer.visibility = View.GONE val params = answerViewHolder.binding.includeVoteAndComment.root.layoutParams as LinearLayout.LayoutParams params.width = 80f.dip2px() answerViewHolder.binding.includeVoteAndComment.root.layoutParams = params + } else { + answerViewHolder.voteCountContainer.visibility = View.VISIBLE + val params = answerViewHolder.binding.includeVoteAndComment.root.layoutParams as LinearLayout.LayoutParams + params.width = 160f.dip2px() + answerViewHolder.binding.includeVoteAndComment.root.layoutParams = params } - } else { val forumSearchHolder = holder as ForumSearchContentListViewHolder - forumSearchHolder.binding.entity = answer - if (answer.type == "question") { - forumSearchHolder.binding.content.visibility = View.GONE - val title = answer.questions.title ?: "" - val spannableStringBuilder = SpannableStringBuilder(" ") - spannableStringBuilder.setSpan(CenterImageSpan(mContext, R.drawable.ic_ask_label), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) - spannableStringBuilder.append(title.fromHtml()) - forumSearchHolder.binding.title.text = spannableStringBuilder - } else { - forumSearchHolder.binding.content.visibility = View.VISIBLE - forumSearchHolder.binding.title.text = answer.questions.title?.fromHtml() - } - when { - answer.getPassVideos().isNotEmpty() -> { - val poster = answer.getPassVideos()[0].poster - ImageUtils.display(forumSearchHolder.binding.image, poster, false) - forumSearchHolder.binding.image.visibility = View.VISIBLE - } - answer.images.isNotEmpty() -> { - forumSearchHolder.binding.image.visibility = View.VISIBLE - ImageUtils.display(forumSearchHolder.binding.image, answer.images[0], false) - } - else -> { - forumSearchHolder.binding.image.visibility = View.GONE - } - } + if (answer.type == "video") { + forumSearchHolder.binding.run { + normalContainer.visibility = View.GONE + includedAnswerItem.root.visibility = View.VISIBLE - forumSearchHolder.binding.content.text = answer.brief?.fromHtml() - forumSearchHolder.itemView.setOnClickListener { - if ("community_article" == answer.type) { - mContext.startActivity(ArticleDetailActivity.getIntent(mContext, CommunityEntity(answer.bbs.id), answer.id!!, mEntrance, "")) + includedAnswerItem.entity = answer + includedAnswerItem.executePendingBindings() + + val answerViewHolder = ForumArticleAskItemViewHolder(includedAnswerItem) + answerViewHolder.bindForumAnswerItem(answer, mEntrance, "") + answerViewHolder.itemView.setOnClickListener { + mContext.startActivity(ForumVideoDetailActivity.getIntent(mContext, answer.id ?:"")) + } + } + } else { + forumSearchHolder.binding.includedAnswerItem.root.visibility = View.GONE + forumSearchHolder.binding.normalContainer.visibility = View.VISIBLE + + forumSearchHolder.binding.entity = answer + if (answer.type == "question") { + forumSearchHolder.binding.content.visibility = View.GONE + val title = answer.questions.title ?: "" + val spannableStringBuilder = SpannableStringBuilder(" ") + spannableStringBuilder.setSpan(CenterImageSpan(mContext, R.drawable.ic_ask_label), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + spannableStringBuilder.append(title.fromHtml()) + forumSearchHolder.binding.title.text = spannableStringBuilder } else { - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, answer.id, mEntrance, "")) + forumSearchHolder.binding.content.visibility = View.VISIBLE + forumSearchHolder.binding.title.text = answer.questions.title?.fromHtml() + } + when { + answer.getPassVideos().isNotEmpty() -> { + val poster = answer.getPassVideos()[0].poster + ImageUtils.display(forumSearchHolder.binding.image, poster, false) + forumSearchHolder.binding.image.visibility = View.VISIBLE + } + answer.images.isNotEmpty() -> { + forumSearchHolder.binding.image.visibility = View.VISIBLE + ImageUtils.display(forumSearchHolder.binding.image, answer.images[0], false) + } + else -> { + forumSearchHolder.binding.image.visibility = View.GONE + } + } + + forumSearchHolder.binding.content.text = answer.brief?.fromHtml() + forumSearchHolder.itemView.setOnClickListener { + if ("community_article" == answer.type) { + mContext.startActivity(ArticleDetailActivity.getIntent(mContext, CommunityEntity(answer.bbs.id), answer.id!!, mEntrance, "")) + } else { + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, answer.id + ?: "", mEntrance, "")) + } } } } diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt index 6b3945f73b..32c8641018 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt @@ -9,6 +9,7 @@ import com.gh.common.constant.Constants import com.gh.common.util.DirectUtils import com.gh.common.util.EntranceUtils import com.gh.common.util.showKeyBoard +import com.gh.common.util.toColor import com.gh.gamecenter.DisplayType import com.gh.gamecenter.R import com.gh.gamecenter.SearchActivity @@ -28,6 +29,7 @@ class ForumOrUserSearchActivity : SearchActivity() { "搜索此论坛中的内容" } searchEt.showKeyBoard() + searchBtn.setTextColor(R.color.theme_font.toColor()) } override fun search(type: SearchType, key: String?) { diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchFragment.kt index 5fbdfd8520..5252a4def9 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchFragment.kt @@ -18,7 +18,7 @@ class ForumOrUserSearchFragment : BaseFragment_TabLayout() { } override fun initTabTitleList(tabTitleList: MutableList) { - tabTitleList.add("帖子") + tabTitleList.add("内容") tabTitleList.add("用户") } @@ -42,10 +42,7 @@ class ForumOrUserSearchFragment : BaseFragment_TabLayout() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - mTabLayout.tabMode = TabLayout.MODE_AUTO - val tabLayoutParams = mTabLayout.layoutParams as RelativeLayout.LayoutParams - tabLayoutParams.width = RelativeLayout.LayoutParams.WRAP_CONTENT - mTabLayout.layoutParams = tabLayoutParams + mTabLayout.tabMode = TabLayout.MODE_FIXED val viewpagerParams = mViewPager.layoutParams as LinearLayout.LayoutParams viewpagerParams.topMargin = 0.5f.dip2px() diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java index 5ca3cc8a52..73075ce82d 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java +++ b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java @@ -49,7 +49,7 @@ import com.gh.gamecenter.entity.SubjectRecommendEntity; import com.gh.gamecenter.eventbus.EBReuse; import com.gh.gamecenter.eventbus.EBSkip; import com.gh.gamecenter.eventbus.EBUISwitch; -import com.gh.gamecenter.forum.home.ForumHomeFragment; +import com.gh.gamecenter.forum.home.CommunityHomeFragment; import com.gh.gamecenter.game.GameFragment; import com.gh.gamecenter.message.MessageUnreadRepository; import com.gh.gamecenter.message.MessageUnreadViewModel; @@ -57,7 +57,6 @@ import com.gh.gamecenter.personal.PersonalFragment; import com.gh.gamecenter.video.detail.HomeVideoFragment; import com.halo.assistant.HaloApp; import com.lightgame.listeners.OnBackPressedListener; -import com.lightgame.utils.Utils; import com.lightgame.view.CheckableImageView; import com.lightgame.view.CheckableLinearLayout; import com.lightgame.view.NoScrollableViewPager; @@ -95,7 +94,7 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem @BindView(R.id.lottieVideo) LottieAnimationView mLottieVideo; @BindView(R.id.lottieCommunity) - LottieAnimationView mLottieCommunity; + LottieAnimationView mLottieBBS; @BindView(R.id.lottieMine) SimpleDraweeView mLottieMine; @BindView(R.id.main_tab_game_name) @@ -103,8 +102,8 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem public static final int INDEX_HOME = 0; public static final int INDEX_GAME = 1; - public static final int INDEX_VIDEO = 2; - public static final int INDEX_ASK = 3; + public static final int INDEX_BBS = 2; + public static final int INDEX_VIDEO = 3; public static final int INDEX_PERSONAL = 4; public static final String EB_MAIN_SCROLL_TOP = "main_scroll_top"; @@ -114,7 +113,7 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem private MainWrapperViewModel mViewModel; private MessageUnreadViewModel mMessageUnreadViewModel; private HomeVideoFragment homeVideoFragment; - private String[] resAssets = {"lottie/tab_home.json", "lottie/tab_game.json", "lottie/tab_video.json", "lottie/tab_forum.json", "tab_mine.gif"}; + private String[] resAssets = {"lottie/tab_home.json", "lottie/tab_game.json", "lottie/tab_forum.json", "lottie/tab_video.json", "tab_mine.gif"}; @Override protected int getLayoutId() { @@ -147,12 +146,13 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem Bundle videoArgs = new Bundle(); videoArgs.putBoolean(EntranceUtils.KEY_IS_HOME_VIDEO, true); homeVideoFragment.setArguments(videoArgs); + + fragments.add(new CommunityHomeFragment()); fragments.add(homeVideoFragment); if (mViewModel.shouldHideVideoTab()) { mTabVideo.setVisibility(View.GONE); } - fragments.add(new ForumHomeFragment()); fragments.add(new PersonalFragment()); } @@ -292,7 +292,7 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem case INDEX_VIDEO: DataUtils.onMtaEvent(getContext(), "顶级页面", "BottomBar_双击", "视频"); break; - case INDEX_ASK: + case INDEX_BBS: DataUtils.onMtaEvent(getContext(), "顶级页面", "BottomBar_双击", "问答"); break; case INDEX_PERSONAL: @@ -328,42 +328,42 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem private void playTabAnimation(int toCheck) { switch (toCheck) { - case 0: + case INDEX_HOME: stopAnimation(mLottieGame, INDEX_GAME); stopAnimation(mLottieVideo, INDEX_VIDEO); - stopAnimation(mLottieCommunity, INDEX_ASK); + stopAnimation(mLottieBBS, INDEX_BBS); stopAnimation(mLottieMine, INDEX_PERSONAL); playAnimation(mLottieHome, toCheck); break; - case 1: + case INDEX_GAME: SubjectRecommendEntity value = mViewModel.getNavBar().getValue(); if (value != null && !TextUtils.isEmpty(value.getAnimationCode())) { stopAnimation(mLottieHome, INDEX_HOME); stopAnimation(mLottieVideo, INDEX_VIDEO); - stopAnimation(mLottieCommunity, INDEX_ASK); + stopAnimation(mLottieBBS, INDEX_BBS); stopAnimation(mLottieMine, INDEX_PERSONAL); playGameAnimation(value, toCheck); } break; - case 2: + case INDEX_BBS: stopAnimation(mLottieHome, INDEX_HOME); stopAnimation(mLottieGame, INDEX_GAME); - stopAnimation(mLottieCommunity, INDEX_ASK); + stopAnimation(mLottieVideo, INDEX_VIDEO); + stopAnimation(mLottieMine, INDEX_PERSONAL); + playAnimation(mLottieBBS, toCheck); + break; + case INDEX_VIDEO: + stopAnimation(mLottieHome, INDEX_HOME); + stopAnimation(mLottieGame, INDEX_GAME); + stopAnimation(mLottieBBS, INDEX_BBS); stopAnimation(mLottieMine, INDEX_PERSONAL); playAnimation(mLottieVideo, toCheck); break; - case 3: + case INDEX_PERSONAL: stopAnimation(mLottieHome, INDEX_HOME); stopAnimation(mLottieGame, INDEX_GAME); stopAnimation(mLottieVideo, INDEX_VIDEO); - stopAnimation(mLottieMine, INDEX_PERSONAL); - playAnimation(mLottieCommunity, toCheck); - break; - case 4: - stopAnimation(mLottieHome, INDEX_HOME); - stopAnimation(mLottieGame, INDEX_GAME); - stopAnimation(mLottieVideo, INDEX_VIDEO); - stopAnimation(mLottieCommunity, INDEX_ASK); + stopAnimation(mLottieBBS, INDEX_BBS); playAnimation(mLottieMine, toCheck); break; default: @@ -477,7 +477,7 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem case INDEX_GAME: tabText = "游戏库"; break; - case INDEX_ASK: + case INDEX_BBS: tabText = "论坛"; LogUtils.uploadAccessBbsTab(); break; diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt index 9b19f67ccb..dbdc1bd0e3 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt @@ -60,6 +60,7 @@ import com.gh.gamecenter.home.video.ScrollCalculatorHelper import com.gh.gamecenter.mvvm.Status import com.gh.gamecenter.normal.NormalFragment import com.gh.gamecenter.packagehelper.PackageViewModel +import com.gh.gamecenter.setting.VideoSettingFragment import com.gh.gamecenter.simulatorgame.SimulatorGameActivity import com.gh.gamecenter.tag.TagsActivity import com.gh.gamecenter.user.UserViewModel @@ -850,17 +851,29 @@ class GameDetailFragment : NormalFragment() { mTopVideoView.updateThumb(topVideo.poster) //val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false) - if (NetworkUtils.isWifiConnected(requireContext()) /*|| !trafficVideo*/) { - if (mViewModel.isTopVideoPartlyCached(topVideo.url)) { + val videoOption = SPUtils.getString(Constants.SP_HOME_OR_DETAIL_VIDEO_OPTION, VideoSettingFragment.VIDEO_OPTION_WIFI) + ?:VideoSettingFragment.VIDEO_OPTION_WIFI + when (videoOption) { + VideoSettingFragment.VIDEO_OPTION_ALL -> { mTopVideoView.startPlayLogic(isAutoPlay = true) - } else { - // 未有缓存时,为避免影响页面加载,延迟自动播放视频 - postDelayedRunnable({ - if (activity != null && activity?.isFinishing != true) { - mTopVideoView.startPlayLogic(isAutoPlay = true) - } - }, INITIAL_DELAY) } + + VideoSettingFragment.VIDEO_OPTION_WIFI -> { + if (NetworkUtils.isWifiConnected(requireContext()) /*|| !trafficVideo*/) { + if (mViewModel.isTopVideoPartlyCached(topVideo.url)) { + mTopVideoView.startPlayLogic(isAutoPlay = true) + } else { + // 未有缓存时,为避免影响页面加载,延迟自动播放视频 + postDelayedRunnable({ + if (activity != null && activity?.isFinishing != true) { + mTopVideoView.startPlayLogic(isAutoPlay = true) + } + }, INITIAL_DELAY) + } + } + } + + else -> {} } mTopVideoView.fullscreenButton.setOnClickListener { diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt index 105fa9ec46..60a0855635 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt @@ -51,7 +51,7 @@ class GameDetailViewModel(application: Application, // 供被包裹 fragment (如 FuliFragment) 用的合并用户数据后的游戏数据 liveData val unifiedGameDetailWithUserRelatedInfoForChildLiveData = MutableLiveData() - var videoIsMuted = true + var videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) var displayTopVideo: Boolean = false fun loadData() { diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/answer/GameDetailAnswerAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/answer/GameDetailAnswerAdapter.kt index 9d8c82f45a..e9a460eb14 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/answer/GameDetailAnswerAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/answer/GameDetailAnswerAdapter.kt @@ -18,7 +18,7 @@ import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.Questions -import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity import com.halo.assistant.HaloApp import com.lightgame.adapter.BaseRecyclerAdapter @@ -59,7 +59,7 @@ class GameDetailAnswerAdapter(context: Context, mContext.startActivity(ArticleDetailActivity.getIntent(mContext, CommunityEntity(entity.articleCommunityId, ""), entity.id!!, mEntrance, path)) } else { MtaHelper.onEvent("游戏详情_新", "点击问题", mViewModel.game?.name + "+" + entity.questions.title) - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, entity.questions.id, mEntrance, path)) + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, entity.questions.id, mEntrance, path)) } } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt index 681d40e623..667e49fe40 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt @@ -173,8 +173,6 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS } fun updateMuteStatus() { - val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_TOP_VIDEO_VOICE, true) - viewModel?.videoIsMuted = topVideoVoiceStatus if (viewModel?.videoIsMuted == true) { mute() } else { @@ -186,7 +184,6 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS viewModel?.videoIsMuted = true volume.setImageResource(R.drawable.ic_game_detail_volume_off) CustomManager.getCustomManager(getKey()).isNeedMute = true - SPUtils.setBoolean(Constants.SP_TOP_VIDEO_VOICE, true) if (isManual) { Utils.toast(context, "当前处于静音状态") uploadVideoStreamingPlaying("点击静音") @@ -198,7 +195,6 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS viewModel?.videoIsMuted = false volume.setImageResource(R.drawable.ic_game_detail_volume_on) CustomManager.getCustomManager(getKey()).isNeedMute = false - SPUtils.setBoolean(Constants.SP_TOP_VIDEO_VOICE, false) if (isManual) { uploadVideoStreamingPlaying("取消静音") MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-解除静音", combinedTitleAndId) diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt index 8d86398bdf..c1f7987273 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt @@ -175,7 +175,7 @@ class HomeFragment : BaseFragment() { if (position != 0L) { mScrollCalculatorHelper.currentPlayer?.seekTo(position) mScrollCalculatorHelper.currentPlayer?.onVideoResume(false) - val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_TOP_VIDEO_VOICE, true) + val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) if (topVideoVoiceStatus) { mScrollCalculatorHelper.currentPlayer?.mute() } else { diff --git a/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt b/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt index 34d7ebc489..4f9e0e80e1 100644 --- a/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt +++ b/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt @@ -59,7 +59,7 @@ class AutomaticVideoView @JvmOverloads constructor(context: Context, attrs: Attr override fun startPlayLogic() { super.startPlayLogic() - val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_TOP_VIDEO_VOICE, true) + val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) if (topVideoVoiceStatus) { violenceUpdateMuteStatus() } diff --git a/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt b/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt index faaa53f789..e383a00c28 100644 --- a/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt +++ b/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt @@ -1,6 +1,5 @@ package com.gh.gamecenter.home.video -import android.content.Context import android.graphics.Rect import android.os.Handler import android.os.Looper @@ -13,9 +12,9 @@ import com.gh.common.util.SPUtils import com.gh.common.util.safelyGetInRelease import com.gh.gamecenter.entity.SimpleVideoEntity import com.gh.gamecenter.home.HomeItemData +import com.gh.gamecenter.setting.VideoSettingFragment import com.gh.gamecenter.video.detail.CustomManager import com.halo.assistant.HaloApp -import com.halo.assistant.fragment.SettingsFragment import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer class ScrollCalculatorHelper(private val playId: Int, private val rangeTop: Int) { @@ -115,15 +114,21 @@ class ScrollCalculatorHelper(private val playId: Int, private val rangeTop: Int) override fun run() { if (gsyBaseVideoPlayer != null && !gsyBaseVideoPlayer!!.isInPlayingState) { - val sp = HaloApp.getInstance().application.getSharedPreferences("${HaloApp.getInstance().application.packageName}_preferences", Context.MODE_PRIVATE) - val isNonWifiPlay = sp.getBoolean(SettingsFragment.TRAFFIC_HOME_VIDEO_SP_KEY, false) - if (!isNonWifiPlay) { - val isWifiConnected = NetworkUtils.isWifiConnected(HaloApp.getInstance().application) - if (isWifiConnected) { + val videoOption = SPUtils.getString(Constants.SP_HOME_OR_DETAIL_VIDEO_OPTION, VideoSettingFragment.VIDEO_OPTION_WIFI) + ?: VideoSettingFragment.VIDEO_OPTION_WIFI + + when (videoOption) { + VideoSettingFragment.VIDEO_OPTION_ALL -> { startPlayLogic(gsyBaseVideoPlayer, topVideo) } - } else { - startPlayLogic(gsyBaseVideoPlayer, topVideo) + + VideoSettingFragment.VIDEO_OPTION_WIFI -> { + if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) { + startPlayLogic(gsyBaseVideoPlayer, topVideo) + } + } + + else -> {} } } } diff --git a/app/src/main/java/com/gh/gamecenter/message/MessageItemViewHolder.java b/app/src/main/java/com/gh/gamecenter/message/MessageItemViewHolder.java index 5383f38001..449e796f1f 100644 --- a/app/src/main/java/com/gh/gamecenter/message/MessageItemViewHolder.java +++ b/app/src/main/java/com/gh/gamecenter/message/MessageItemViewHolder.java @@ -34,6 +34,7 @@ import com.gh.gamecenter.qa.comment.CommentActivity; import com.gh.gamecenter.qa.entity.Questions; import com.gh.gamecenter.qa.follow.AskFollowMoreDialog; import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity; +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity; import com.gh.gamecenter.retrofit.Response; import com.gh.gamecenter.retrofit.RetrofitManager; import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel; @@ -527,7 +528,7 @@ public class MessageItemViewHolder extends BaseRecyclerViewHolder case "invited": if (view.getId() == R.id.message_original || view.getId() == R.id.message_item) { MessageEntity.Question question = entity.getQuestion(); - context.startActivity(QuestionsDetailActivity.getIntent(context, question.getId(), entrance, path)); + context.startActivity(NewQuestionDetailActivity.getIntent(context, question.getId(), entrance, path)); Questions questions = new Questions(); questions.setId(question.getId()); @@ -539,7 +540,7 @@ public class MessageItemViewHolder extends BaseRecyclerViewHolder case "follow_question": if (view.getId() == R.id.message_original) { MessageEntity.Question question = entity.getQuestion(); - context.startActivity(QuestionsDetailActivity.getIntent(context, question.getId(), entrance, path)); + context.startActivity(NewQuestionDetailActivity.getIntent(context, question.getId(), entrance, path)); Questions questions = new Questions(); questions.setId(question.getId()); @@ -579,13 +580,13 @@ public class MessageItemViewHolder extends BaseRecyclerViewHolder case "community_article_comment_vote": community = new CommunityEntity(entity.getArticle().getCommunityId(), ""); if (view.getId() == R.id.message_original || view.getId() == R.id.message_item) { - context.startActivity(CommentActivity.getArticleDetailCommentIntent(context, entity.getComment().getId(), community.getId(), entity.getArticle().getId(), false, 1, entrance, path)); + context.startActivity(CommentActivity.getCommentDetailIntent(context, entity.getComment().getId(), community.getId(), entity.getArticle().getId(), "", "",false, 1, entrance, path)); } break; case "community_article_comment_reply_vote": community = new CommunityEntity(entity.getArticle().getCommunityId(), ""); if (view.getId() == R.id.message_original || view.getId() == R.id.message_item) { - context.startActivity(CommentActivity.getArticleDetailCommentIntent(context, entity.getComment().getTopId(), community.getId(), entity.getArticle().getId(), false, 1, entrance, path)); + context.startActivity(CommentActivity.getCommentDetailIntent(context, entity.getComment().getTopId(), community.getId(), entity.getArticle().getId(), "", "",false, 1, entrance, path)); } break; case "community_article_comment": @@ -604,7 +605,7 @@ public class MessageItemViewHolder extends BaseRecyclerViewHolder break; case "reply_community_article_comment": if (view.getId() == R.id.message_original || view.getId() == R.id.message_item) { - context.startActivity(CommentActivity.getArticleDetailCommentIntent(context, entity.getDialogue().getTo().getTopId(), entity.getArticle().getCommunityId(), entity.getArticle().getId(), false, 1, entrance, path)); + context.startActivity(CommentActivity.getCommentDetailIntent(context, entity.getDialogue().getTo().getTopId(), entity.getArticle().getCommunityId(), entity.getArticle().getId(), "", "",false, 1, entrance, path)); } break; case "game_comment_vote": diff --git a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameFragment.kt b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameFragment.kt index 96df1f5f60..7f4ae25c1d 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameFragment.kt @@ -59,7 +59,7 @@ open class PlayedGameFragment : ListFragment() override fun provideListViewModel(): PlayedGameViewModel { val userId = arguments?.getString(EntranceUtils.KEY_USER_ID) ?: UserManager.getInstance().userId - mViewModel = viewModelProvider(PlayedGameViewModel.Factory(HaloApp.getInstance().application, userId)) + mViewModel = viewModelProvider(PlayedGameViewModel.Factory(userId)) return mViewModel } diff --git a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt index f01329c6a8..02bf110a82 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameViewModel.kt @@ -8,6 +8,7 @@ import com.gh.gamecenter.baselist.ListViewModel import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp import com.lightgame.utils.Utils import io.reactivex.Observable import io.reactivex.Single @@ -56,10 +57,9 @@ class PlayedGameViewModel(application: Application, var userId: String) }) } - class Factory(private val mApplication: Application, - private val mUserId: String) : ViewModelProvider.NewInstanceFactory() { + class Factory(private val mUserId: String) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { - return PlayedGameViewModel(mApplication, mUserId) as T + return PlayedGameViewModel(HaloApp.getInstance().application, mUserId) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt index fd96233d32..9f61e07cd8 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt @@ -2,31 +2,31 @@ package com.gh.gamecenter.personalhome import android.view.View import com.gh.common.util.MtaHelper -import com.gh.gamecenter.databinding.PersonalHomeItemBinding +import com.gh.gamecenter.databinding.UserHistoryItemBinding import com.gh.gamecenter.entity.PersonalHistoryEntity import com.gh.gamecenter.forum.detail.ForumDetailActivity import com.gh.gamecenter.qa.answer.BaseAnswerOrArticleItemViewHolder -import com.gh.gamecenter.qa.questions.CommunityQuestionViewHolder -class PersonalItemViewHolder(val binding: PersonalHomeItemBinding) : BaseAnswerOrArticleItemViewHolder(binding.root) { +class PersonalItemViewHolder(val binding: UserHistoryItemBinding) : BaseAnswerOrArticleItemViewHolder(binding.root) { fun bindPersonalItem(entity: PersonalHistoryEntity, entrance: String) { commentCountContainer.isClickable = true - if (entity.type.contains("question")) { - voteCountContainer.visibility = View.GONE - CommunityQuestionViewHolder.bindAnswerCount( - entity.transformQuestionEntity(), - commentCountContainer, - commentCount, - entrance) - } else { +// if (entity.type.contains("question")) { +// voteCountContainer.visibility = View.GONE +// CommunityQuestionViewHolder.bindAnswerCount( +// entity.transformQuestionEntity(), +// commentCountContainer, +// commentCount, +// entrance) +// } else { voteCountContainer.visibility = View.VISIBLE bindCommendAndVote(entity.transformAnswerEntity(), entrance) - } +// } - forumNameTv.setOnClickListener { - MtaHelper.onEvent(getEventId(entrance), getKey(entrance), entity.community.name) - itemView.context.startActivity(ForumDetailActivity.getIntent(itemView.context, entity.community.id, entrance)) - } + forumNameLl?.visibility = View.GONE +// forumNameContainer?.setOnClickListener { +// MtaHelper.onEvent(getEventId(entrance), getKey(entrance), entity.community.name) +// itemView.context.startActivity(ForumDetailActivity.getIntent(itemView.context, entity.community.id, entrance)) +// } } } diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt index acd0c6b4ba..05ca0e8a3e 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt @@ -15,19 +15,15 @@ import androidx.constraintlayout.widget.ConstraintSet import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager import butterknife.OnClick import com.gh.base.adapter.FragmentAdapter import com.gh.base.fragment.BaseFragment_TabLayout import com.gh.common.constant.Constants import com.gh.common.util.* import com.gh.common.util.DirectUtils.directToBadgeWall -import com.gh.common.view.HorizontalItemDecoration -import com.gh.download.DownloadManager import com.gh.gamecenter.* import com.gh.gamecenter.databinding.FragmentHomeBinding import com.gh.gamecenter.entity.* -import com.gh.gamecenter.manager.PackagesManager import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.message.MessageUnreadViewModel import com.gh.gamecenter.normal.NormalFragment @@ -35,10 +31,8 @@ import com.gh.gamecenter.personalhome.background.PersonalityBackgroundActivity import com.gh.gamecenter.personalhome.border.AvatarBorderActivity import com.gh.gamecenter.personalhome.fans.FansActivity import com.gh.gamecenter.personalhome.followers.FollowersActivity -import com.gh.gamecenter.personalhome.home.UserCommentHistoryFragment -import com.gh.gamecenter.personalhome.home.UserHistoryFragment -import com.gh.gamecenter.personalhome.home.UserHistoryViewModel -import com.gh.gamecenter.personalhome.home.UserVideoHistoryFragment +import com.gh.gamecenter.personalhome.home.* +import com.gh.gamecenter.personalhome.home.game.UserGameFragment import com.gh.gamecenter.user.UserViewModel import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener @@ -86,7 +80,6 @@ class UserHomeFragment : NormalFragment() { mUserHomeViewModel.run { getUserInfo() getBadgeList() - getUserPlayedGame() getUserPlayedGameCount() if (BuildConfig.DEBUG || BuildConfig.BUILD_TIME != 0L) { getUserLevel() @@ -189,40 +182,11 @@ class UserHomeFragment : NormalFragment() { reuse_no_connection.visibility = View.VISIBLE reuse_no_connection.setOnClickListener { mUserHomeViewModel.getUserInfo() - mUserHomeViewModel.getUserPlayedGame() mUserHomeViewModel.getUserPlayedGameCount() } toast("网络异常") } - mUserHomeViewModel.playedGames.observe(this, Observer { - if (!it.isNullOrEmpty()) { - mPlayGameCount = it.size - changeLayoutForPlayedGameVisible(true) - showPlayedGames(it) - } else { - changeLayoutForPlayedGameVisible(false) - } - val installedList = PackagesManager.filterSameApk(PackagesManager.filterDownloadBlackPackage(PackagesManager.getInstalledList())) - val simulatorDownloadEntityList = DownloadManager.getInstance(requireContext()).allSimulatorDownloadEntity - simulatorDownloadEntityList.forEach { entity -> - installedList.add(GameInstall(entity.gameId, packageName = entity.packageName, name = entity.name, icon = entity.icon)) - } - //val count = SPUtils.getInt(Constants.SP_MARK_INSTALLED_GAME, 0) - val isCancel = SPUtils.getBoolean(Constants.SP_MARK_INSTALLED_GAME_USER_HOME, false) - if (mUserHomeViewModel.userId == UserManager.getInstance().userId - && !isCancel - && it.isNullOrEmpty() - && installedList.isNotEmpty()) { - mDialog = InstalledGameDialog(requireContext(), installedList, "个人主页详情", "标记玩过弹窗") - mDialog.show() - mDialog.onConfirmClickListener = { - mUserHomeViewModel.getUserPlayedGame() - mUserHomeViewModel.getUserPlayedGameCount() - } - } - }) - // 记录玩过的游戏总数 mUserHomeViewModel.playGamesCount.observe(this, Observer { mPlayGameCount = it @@ -266,60 +230,30 @@ class UserHomeFragment : NormalFragment() { } } - private fun changeLayoutForPlayedGameVisible(isVisible: Boolean) { + private fun changeLayoutForContent() { mHomeBinding?.run { - if (isVisible) { - playedGameEmptyVIew.visibility = View.GONE + val set = ConstraintSet() + set.run { + clone(contentContainer) + clear(R.id.user_icon, ConstraintSet.BOTTOM) + connect(R.id.user_icon, ConstraintSet.TOP, R.id.statusBarView, ConstraintSet.BOTTOM, 50F.dip2px()) + applyTo(contentContainer) + } - val set = ConstraintSet() - set.run { + userCountContainer.post { + val newHeight = userCountContainer.bottom + (32F + 16F).dip2px() + userBackgroundContainer.layoutParams = userBackgroundContainer.layoutParams.apply { + height = newHeight + } + + val newSet = ConstraintSet() + newSet.run { clone(contentContainer) - clear(R.id.user_icon, ConstraintSet.BOTTOM) - connect(R.id.user_icon, ConstraintSet.TOP, R.id.statusBarView, ConstraintSet.BOTTOM, 50F.dip2px()) + clear(R.id.user_icon, ConstraintSet.TOP) + val marginBottom = newHeight - DisplayUtils.getStatusBarHeight(resources) - 50F.dip2px() - 96F.dip2px() + connect(R.id.user_icon, ConstraintSet.BOTTOM, R.id.user_background_container, ConstraintSet.BOTTOM, marginBottom) applyTo(contentContainer) } - - playedGameContainer.post { - val newHeight = playedGameContainer.bottom - userBackgroundContainer.layoutParams = userBackgroundContainer.layoutParams.apply { - height = newHeight - } - - val newSet = ConstraintSet() - newSet.run { - clone(contentContainer) - clear(R.id.user_icon, ConstraintSet.TOP) - val marginBottom = newHeight - DisplayUtils.getStatusBarHeight(resources) - 50F.dip2px() - 96F.dip2px() - connect(R.id.user_icon, ConstraintSet.BOTTOM, R.id.user_background_container, ConstraintSet.BOTTOM, marginBottom) - applyTo(contentContainer) - } - } - } else { - playedGameEmptyVIew.visibility = View.VISIBLE - - val set = ConstraintSet() - set.run { - clone(contentContainer) - clear(R.id.user_icon, ConstraintSet.BOTTOM) - connect(R.id.user_icon, ConstraintSet.TOP, R.id.statusBarView, ConstraintSet.BOTTOM, 50F.dip2px()) - applyTo(contentContainer) - } - - userCountContainer.post { - val newHeight = userCountContainer.bottom + (32F + 16F).dip2px() - userBackgroundContainer.layoutParams = userBackgroundContainer.layoutParams.apply { - height = newHeight - } - - val newSet = ConstraintSet() - newSet.run { - clone(contentContainer) - clear(R.id.user_icon, ConstraintSet.TOP) - val marginBottom = newHeight - DisplayUtils.getStatusBarHeight(resources) - 50F.dip2px() - 96F.dip2px() - connect(R.id.user_icon, ConstraintSet.BOTTOM, R.id.user_background_container, ConstraintSet.BOTTOM, marginBottom) - applyTo(contentContainer) - } - } } } } @@ -360,23 +294,22 @@ class UserHomeFragment : NormalFragment() { // 根据是否有内容选择页面显示 when { count.gameComment > 0 -> 0 - count.getQaCount() > 0 -> 1 - count.video > 0 -> 2 + count.getTotalCount() > 0 -> 1 +// count.video > 0 -> 2 else -> 0 } } val tag = "android:switcher:${mHomeBinding?.viewpager?.id}:" - val commentFragment = childFragmentManager.findFragmentByTag("${tag}0") - ?: UserCommentHistoryFragment.getInstance(mUserHomeViewModel.userId) + val gameFragment = childFragmentManager.findFragmentByTag("${tag}0") + ?: UserGameFragment.getInstance(mUserHomeViewModel.userId, count.gameComment) val qaFragment = childFragmentManager.findFragmentByTag("${tag}1") ?: UserHistoryFragment.getInstance(mUserHomeViewModel.userId, UserHistoryViewModel.SCENE.QUESTION_ANSWER, count) - val videoFragment = childFragmentManager.findFragmentByTag("${tag}2") - ?: UserVideoHistoryFragment.getInstance(mUserHomeViewModel.userId, count) +// val videoFragment = childFragmentManager.findFragmentByTag("${tag}2") +// ?: UserVideoHistoryFragment.getInstance(mUserHomeViewModel.userId, count) - val fragmentList = listOf(commentFragment, qaFragment, videoFragment) - val titleList = listOf("游戏评论", "论坛", "视频") - val countList = listOf(count.gameComment, count.getQaCount(), count.video) + val fragmentList = listOf(gameFragment, qaFragment) + val titleList = listOf("游戏", "发布") viewpager.offscreenPageLimit = fragmentList.size viewpager.adapter = FragmentAdapter(childFragmentManager, fragmentList, titleList) @@ -388,40 +321,21 @@ class UserHomeFragment : NormalFragment() { for (i in 0 until tabLayout.tabCount) { val tab = tabLayout.getTabAt(i) ?: continue - val tabView = getTabView(titleList[i], countList[i]) + val tabView = getTabView(titleList[i]) tab.customView = tabView } BaseFragment_TabLayout.initTabStyle(tabLayout, viewpager.currentItem) } - private fun getTabView(title: String, count: Int): View { + private fun getTabView(title: String): View { val view = LayoutInflater.from(HaloApp.getInstance().application.baseContext).inflate(R.layout.tab_item_user_home, null) val tabTitle = view.findViewById(R.id.tab_title) - val tabCount = view.findViewById(R.id.tab_count) if (tabTitle is CheckedTextView) { tabTitle.text = title } - tabCount.text = if (count == 0) "" else count.toString() return view } - private fun showPlayedGames(playedGames: List) { - checkMoreTv.goneIf(playedGames.size <= 3) - checkMoreTv.setOnClickListener { - MtaHelper.onEvent("个人主页详情", "个人主页详情", "查看更多") - DirectUtils.directToPlayedGame(checkMoreTv.context, mUserHomeViewModel.userId, mEntrance, mPath) - } - - val adapter = UserHomePlayedGameAdapter(playedGames) - - playedGameContainer.visibility = View.VISIBLE - playedGameRecyclerView.adapter = adapter - playedGameRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) - playedGameRecyclerView.addItemDecoration(HorizontalItemDecoration(requireContext(), 16, playedGames.size, true)) - - adapter.notifyDataSetChanged() - } - private fun trackMtaEvent(name: String? = "") { MtaHelper.onEvent("个人主页", mPath, StringUtils.combineTwoString(name, mUserHomeViewModel.userId)) MtaHelper.onEvent("个人主页", "不区分位置", StringUtils.combineTwoString(name, mUserHomeViewModel.userId)) @@ -440,7 +354,7 @@ class UserHomeFragment : NormalFragment() { entity = personalData executePendingBindings() - changeLayoutForPlayedGameVisible(playedGameContainer.visibility == View.VISIBLE) + changeLayoutForContent() // personalData.auth?.let { // userAuthCommand.text = it.text // userAuthCommand.setTextColor(Color.parseColor("#" + it.color)) @@ -479,6 +393,7 @@ class UserHomeFragment : NormalFragment() { if (mUserHomeViewModel.userId != UserManager.getInstance().userId) { ivMore.visibility = View.VISIBLE ivShare.visibility = View.GONE + changeBgTips.visibility = View.GONE userFansCountHint.visibility = View.GONE if (personalData.me.isFollower) { userConcernedBtn.visibility = View.VISIBLE @@ -497,11 +412,27 @@ class UserHomeFragment : NormalFragment() { DirectUtils.directToHomeActivity(requireContext(), personalData.lastVisitor?.id, "个人主页-最近来访") } + // 默认显示悬浮窗,点击图标进入更换背景页,悬浮窗消失 + // 若未点击图标,当天内不再显示,第2天后进入继续显示悬浮窗 + changeBgTips.visibility = when { + SPUtils.getBoolean(Constants.SP_HAS_CLICK_CHANGE_BG) -> View.GONE + + SPUtils.getBoolean(Constants.SP_SHOW_CHANGE_BG_TIPS, true) -> { + SPUtils.setBoolean(Constants.SP_SHOW_CHANGE_BG_TIPS, false) + View.VISIBLE + } + + else -> View.GONE + } + // 跳转更换背景页 userChangeBgBtn.setOnClickListener { IntegralLogHelper.log("click_change _background", LOCATION) + SPUtils.setBoolean(Constants.SP_HAS_CLICK_CHANGE_BG, false) + changeBgTips.visibility = View.GONE startActivity(PersonalityBackgroundActivity.getIntent(requireContext())) } + changeBgTips.setOnClickListener { userChangeBgBtn.performClick() } // 个性签名 userIntroduce.setOnClickListener { diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeViewModel.kt b/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeViewModel.kt index 84074bb0ef..1e070fa220 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeViewModel.kt @@ -30,7 +30,6 @@ class UserHomeViewModel(application: Application, var userId: String) : AndroidV var userInfo = MutableLiveData() var networkError = MutableLiveData() - var playedGames = MutableLiveData>() var badges = MutableLiveData>() var availableBadge = MutableLiveData() var availableBadgeCount = MutableLiveData() @@ -64,34 +63,6 @@ class UserHomeViewModel(application: Application, var userId: String) : AndroidV followingCommand(false) } - @SuppressLint("CheckResult") - fun getUserPlayedGame() { - RetrofitManager.getInstance(getApplication()) - .api.getPlayedGames(userId, 1, Utils.getTime(getApplication())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - for (game in data) { - if (game.name!!.length > 6) { - game.name = game.name!!.substring(0, 6) + "..." - game.nameSuffix = "" - } - } - - if (data.isNotEmpty()) { - playedGames.postValue(data.take(10)) - } else { - playedGames.postValue(null) - } - } - - override fun onFailure(exception: Exception) { - super.onFailure(exception) - playedGames.postValue(null) - } - }) - } @SuppressLint("CheckResult") fun getUserPlayedGameCount() { diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/HorizontalExcellentCommentsAdapter.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/HorizontalExcellentCommentsAdapter.kt deleted file mode 100644 index a6b08cb1f3..0000000000 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/HorizontalExcellentCommentsAdapter.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.gh.gamecenter.personalhome.home - -import android.content.Context -import android.view.ViewGroup -import com.gh.common.util.* -import com.gh.gamecenter.R -import com.gh.gamecenter.adapter.viewholder.UserHomeAmwayItemViewHolder -import com.gh.gamecenter.databinding.UserHomeAmwayItemBinding -import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity -import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity -import com.gh.gamecenter.personalhome.rating.MyRating -import com.lightgame.adapter.BaseRecyclerAdapter -import java.util.regex.Pattern - -class HorizontalExcellentCommentsAdapter(context: Context, - var dataList: List) : BaseRecyclerAdapter(context) { - - var mMaxWidth = context.resources.displayMetrics.widthPixels - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserHomeAmwayItemViewHolder { - val view = mLayoutInflater.inflate(R.layout.user_home_amway_item, parent, false) - return UserHomeAmwayItemViewHolder(UserHomeAmwayItemBinding.bind(view)) - } - - override fun onBindViewHolder(holder: UserHomeAmwayItemViewHolder, position: Int) { - holder.binding.run { - data = dataList[position] - var rating = dataList[position] - - root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply { - leftMargin = if (position == 0) 16F.dip2px() else 12F.dip2px() - rightMargin = if (position == itemCount - 1) 16F.dip2px() else 0 - } - - holder.itemView.layoutParams?.width = if (position == itemCount - 1) { - if (itemCount == 1) mMaxWidth - 32F.dip2px() else mMaxWidth - 16F.dip2px() - } else mMaxWidth - 60F.dip2px() - - gameIcon.displayGameIcon(rating.game.getRawIconIfExisted(), rating.game.iconSubscript) - - val m = Pattern.compile(RatingEditActivity.LABEL_REGEX).matcher(rating.content) - if (m.find()) { - val contents = TextHelper.getCommentLabelSpannableStringBuilder(rating.content, R.color.theme_font) - content.text = contents - } else { - content.text = rating.content - } - - commentBackground.setOnClickListener { - DirectUtils.directToGameDetail(mContext, rating.game.id, "个人主页-优秀评论") - } - - ratingBlock.setOnClickListener { - val intent = RatingReplyActivity.getIntent(mContext, rating.game.id, rating.id, false, "个人主页-优秀评论", "") - - SyncDataBetweenPageHelper.startActivityForResult(mContext, intent, 100, position) - } - } - } - - override fun getItemCount() = dataList.size -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryItemData.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryItemData.kt deleted file mode 100644 index 298308a6d9..0000000000 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryItemData.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.gh.gamecenter.personalhome.home - -import com.gh.gamecenter.personalhome.rating.MyRating - -data class UserCommentHistoryItemData(var excellentComments: List? = null, - var title: String? = null, - var normalComment: MyRating? = null) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt index 9eadff210e..08486ac404 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt @@ -1,5 +1,6 @@ package com.gh.gamecenter.personalhome.home +import android.app.Activity import android.content.Context import android.text.SpannableStringBuilder import android.util.SparseBooleanArray @@ -13,18 +14,23 @@ import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.adapter.viewholder.PersonalHomeRatingViewHolder import com.gh.gamecenter.baselist.ListAdapter -import com.gh.gamecenter.databinding.PersonalHomeItemBinding import com.gh.gamecenter.databinding.PersonalHomeRatingBinding +import com.gh.gamecenter.databinding.UserHistoryItemBinding +import com.gh.gamecenter.entity.ForumVideoEntity import com.gh.gamecenter.entity.PersonalHistoryEntity +import com.gh.gamecenter.forum.home.ArticleItemVideoView import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity import com.gh.gamecenter.personalhome.PersonalItemViewHolder import com.gh.gamecenter.qa.entity.AnswerEntity +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack +import com.shuyu.gsyvideoplayer.utils.OrientationUtils import java.util.regex.Pattern class UserHistoryAdapter(context: Context, private val mListViewModel: UserHistoryViewModel, private val mEntrance: String, - val itemClickCallback: (historyEntity: PersonalHistoryEntity, position: Int) -> Unit) + private val itemClickCallback: (historyEntity: PersonalHistoryEntity, position: Int) -> Unit) : ListAdapter(context) { private var mExpandSparseBooleanArray = SparseBooleanArray() @@ -49,8 +55,8 @@ class UserHistoryAdapter(context: Context, PersonalHomeRatingViewHolder(PersonalHomeRatingBinding.bind(view)) } else -> { - view = mLayoutInflater.inflate(R.layout.personal_home_item, parent, false) - PersonalItemViewHolder(PersonalHomeItemBinding.bind(view)) + view = mLayoutInflater.inflate(R.layout.user_history_item, parent, false) + PersonalItemViewHolder(UserHistoryItemBinding.bind(view)) } } } @@ -80,11 +86,16 @@ class UserHistoryAdapter(context: Context, answer.communityId = historyEntity.community.id answer.communityName = historyEntity.community.name holder.binding.imageContainer.bindData(answer, mEntrance) + if (historyEntity.type == "video") { + bindVideoData(holder.binding, historyEntity.transformForumVideoEntity()) + } else { + bindArticleVideoData(holder.binding, historyEntity) + } holder.binding.run { entity = historyEntity - userName.visibility = View.GONE - forumNameTv.text = entity?.community?.name +// userName.visibility = View.GONE + topLine.goneIf(position == 0) userIcon.display(historyEntity.user?.border, historyEntity.user?.icon, historyEntity.user?.auth?.icon) executePendingBindings() @@ -96,13 +107,22 @@ class UserHistoryAdapter(context: Context, .append(videoSpan) } - val command = getUserCommand(historyEntity.type, historyEntity.time, historyEntity.isEdit) - userCommand.text = if (command.isNotEmpty()) command else historyEntity?.user?.name + "" + userCommand.text = getUserCommand(historyEntity.type, historyEntity.isEdit) questionTitle.setOnClickListener { holder.itemView.performClick() } + historyEntity.user?.run { + userBadgeName.setOnClickListener { userBadgeIcon.performClick() } + userBadgeIcon.setOnClickListener { + DialogUtils.showViewBadgeDialog(mContext, badge) { + DirectUtils.directToBadgeWall(mContext, id, name, icon) + } + } + } + + // 禁止click事件穿透 userIcon.setOnClickListener {} userCommand.setOnClickListener {} @@ -112,6 +132,131 @@ class UserHistoryAdapter(context: Context, } } + private fun bindVideoData(binding: UserHistoryItemBinding, entity: ForumVideoEntity) { + binding.run { + if (entity.url.isEmpty()) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.GONE + } else { + val videoInfo = entity.videoInfo + val visibleView = if (videoInfo.height > videoInfo.width) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.VISIBLE + verticalVideoView + } else { + horizontalVideoView.visibility = View.VISIBLE + verticalVideoView.visibility = View.GONE + horizontalVideoView + } + + val orientationUtils = OrientationUtils(mContext as Activity, visibleView) + orientationUtils.isEnable = false + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(entity.url) + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .setEnlargeImageRes(R.drawable.ic_game_detail_enter_full_screen) + .setShrinkImageRes(R.drawable.ic_game_detail_exit_full_screen) + .setVideoAllCallBack(object : GSYSampleCallBack() { + override fun onQuitFullscreen(url: String?, vararg objects: Any) { + orientationUtils.backToProtVideo() + visibleView.uploadVideoStreamingPlaying("退出全屏") + } + }) + .build(visibleView) + visibleView.run { + updateVideoData(entity) + updateThumb(entity.poster) + updateDurationTv(TimeUtils.formatDuration(entity.length)) + + fullscreenButton.setOnClickListener { + val horizontalVideoView = startWindowFullscreen(mContext, true, true) as? ArticleItemVideoView + if (horizontalVideoView == null) { + toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤") + return@setOnClickListener + } + orientationUtils.resolveByClick() + horizontalVideoView.uuid = uuid + horizontalVideoView.updateVideoData(entity) + horizontalVideoView.updateThumb(entity.poster) + horizontalVideoView.violenceUpdateMuteStatus() + horizontalVideoView.setFullViewStatus() + uploadVideoStreamingPlaying("开始播放") + uploadVideoStreamingPlaying("点击全屏") + } + } + } + } + } + + private fun bindArticleVideoData(binding: UserHistoryItemBinding, entity: PersonalHistoryEntity) { + binding.run { + if (entity.getPassVideos().isNullOrEmpty()) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.GONE + } else { + val video = entity.getPassVideos()[0] + val visibleView = if (video.height > video.width) { + horizontalVideoView.visibility = View.GONE + verticalVideoView.visibility = View.VISIBLE + verticalVideoView + } else { + horizontalVideoView.visibility = View.VISIBLE + verticalVideoView.visibility = View.GONE + horizontalVideoView + } + + val orientationUtils = OrientationUtils(mContext as Activity, visibleView) + orientationUtils.isEnable = false + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(video.url) + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .setEnlargeImageRes(R.drawable.ic_game_detail_enter_full_screen) + .setShrinkImageRes(R.drawable.ic_game_detail_exit_full_screen) + .setVideoAllCallBack(object : GSYSampleCallBack() { + override fun onQuitFullscreen(url: String?, vararg objects: Any) { + orientationUtils.backToProtVideo() + visibleView.uploadVideoStreamingPlaying("退出全屏") + } + }) + .build(visibleView) + visibleView.run { + val forumVideoEntity = entity.transformForumVideoEntity() + updateVideoData(forumVideoEntity) + updateThumb(video.poster) + updateDurationTv(video.duration) + + fullscreenButton.setOnClickListener { + val horizontalVideoView = startWindowFullscreen(mContext, true, true) as? ArticleItemVideoView + if (horizontalVideoView == null) { + toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤") + return@setOnClickListener + } + orientationUtils.resolveByClick() + horizontalVideoView.uuid = uuid + horizontalVideoView.updateVideoData(forumVideoEntity) + horizontalVideoView.updateThumb(video.poster) + horizontalVideoView.violenceUpdateMuteStatus() + horizontalVideoView.setFullViewStatus() + uploadVideoStreamingPlaying("开始播放") + uploadVideoStreamingPlaying("点击全屏") + } + } + } + } + } + private fun bindRatingItem(holder: PersonalHomeRatingViewHolder, position: Int) { val historyEntity = mEntityList[holder.adapterPosition] holder.binding.entity = historyEntity @@ -170,15 +315,15 @@ class UserHistoryAdapter(context: Context, } companion object { - fun getUserCommand(type: String, time: Long, isEdit: Boolean = false): String { + fun getUserCommand(type: String, isEdit: Boolean = false): String { return when (type) { - "answer" -> NewsUtils.getFormattedTime(time) + if (isEdit) " 修改了回答" else " 发布了回答" - "question" -> NewsUtils.getFormattedTime(time) + if (isEdit) " 修改了问题" else " 提交了问题" - "answer_vote" -> NewsUtils.getFormattedTime(time) + " 赞同了回答" - "follow_question" -> NewsUtils.getFormattedTime(time) + " 关注了问题" - "community_article_vote" -> NewsUtils.getFormattedTime(time) + " 赞同了帖子" - "community_article" -> NewsUtils.getFormattedTime(time) + if (isEdit) " 修改了帖子" else " 发布了帖子" - "update-answer" -> NewsUtils.getFormattedTime(time) + " 更新了回答" + "answer" -> if (isEdit) "修改了回答" else "发布了回答" + "question" -> if (isEdit) "修改了问题" else "提交了问题" + "answer_vote" -> "赞同了回答" + "follow_question" -> "关注了问题" + "community_article_vote" -> "赞同了帖子" + "community_article" -> if (isEdit) "修改了帖子" else "发布了帖子" + "update-answer" -> "更新了回答" else -> "" } } diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt index 98967e7822..6ad503ba5a 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt @@ -2,21 +2,16 @@ package com.gh.gamecenter.personalhome.home import android.app.Activity import android.content.Intent -import android.graphics.Color import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.widget.LinearLayout import android.widget.TextView -import androidx.core.content.ContextCompat import androidx.core.os.bundleOf -import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.* -import com.gh.common.view.BugFixedPopupWindow -import com.gh.common.view.VerticalItemDecoration import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.databinding.FragmentUserPublishBinding +import com.gh.gamecenter.entity.ForumVideoEntity import com.gh.gamecenter.entity.PersonalEntity import com.gh.gamecenter.entity.PersonalHistoryEntity import com.gh.gamecenter.entity.RatingComment @@ -26,9 +21,9 @@ import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.entity.AnswerDetailEntity import com.gh.gamecenter.qa.entity.ArticleDetailEntity import com.gh.gamecenter.qa.entity.QuestionsDetailEntity -import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity import com.halo.assistant.HaloApp -import kotlinx.android.synthetic.main.fragment_user_history.* class UserHistoryFragment : ListFragment() { @@ -38,10 +33,12 @@ class UserHistoryFragment : ListFragment= childCount) return + + for (i in 0 until childCount) { + val typeTv = getChildAt(i) as TextView + typeTv.run { + if (i == index) { + setBackgroundResource(R.drawable.button_round_f0f8ff) + setTextColor(R.color.theme_font.toColor()) + } else { + setBackgroundResource(R.drawable.button_round_fafafa) + setTextColor(R.color.text_333333.toColor()) + } + } + } - updateFilterHintText() - filter.background = ContextCompat.getDrawable(requireContext(), R.color.white) - filterContainer.visibleIf(mScene == UserHistoryViewModel.SCENE.QUESTION_ANSWER) - filterContainer.setOnClickListener { - showFilterWindow() - MtaHelper.onEvent("个人主页详情", "个人主页详情", "问答筛选") } } @@ -80,11 +135,14 @@ class UserHistoryFragment : ListFragment "全部内容(${mCount.getQaCount()})" - UserHistoryViewModel.TYPE.QUESTION -> "提问(${mCount.question})" - UserHistoryViewModel.TYPE.ANSWER -> "回答(${mCount.answer})" - UserHistoryViewModel.TYPE.COMMUNITY_ARTICLE -> "帖子(${mCount.communityArticle})" - } - } - } - - private fun showFilterWindow() { - val contentList = UserHistoryViewModel.TYPE.TYPE_LIST - - val inflater = LayoutInflater.from(requireContext()) - val layout = inflater.inflate(R.layout.layout_popup_container, null) - val popupWindow = BugFixedPopupWindow( - layout, - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - - val container = layout.findViewById(R.id.container) - for (text in contentList) { - val item = inflater.inflate(R.layout.layout_popup_option_item, container, false) - - container.addView(item) - - val hintTv = item.findViewById(R.id.hint_text) - hintTv.text = text - - if (mViewModel.type.getDisplayText() == text) { - hintTv.setTextColor(Color.parseColor("#1383EB")) - } - - item.setOnClickListener { - MtaHelper.onEvent("个人主页详情", "个人主页详情", "问答-${text}") - forumFilter.text = text - mViewModel.changeType(UserHistoryViewModel.TYPE.typeFromDisplayText(text)) - popupWindow.dismiss() - } - } - - popupWindow.setOnDismissListener { - filterArrow.rotation = 0f - } - - popupWindow.isTouchable = true - popupWindow.isFocusable = true - - filterArrow.rotation = 180f - popupWindow.showAutoOrientation(filterContainer, DisplayUtils.dip2px(16f)) - } - - override fun onLoadDone() { - super.onLoadDone() - - updateFilterHintText() - filter.visibility = View.VISIBLE - } + override fun getItemDecoration() = null override fun onLoadEmpty() { super.onLoadEmpty() @@ -168,10 +161,9 @@ class UserHistoryFragment : ListFragment filter.visibility = View.GONE + UserHistoryViewModel.TYPE.ALL -> mBinding?.typeScrollView?.visibility = View.GONE else -> { - filter.visibility = View.VISIBLE - updateFilterHintText() + mBinding?.typeScrollView?.visibility = View.VISIBLE } } } @@ -203,35 +195,37 @@ class UserHistoryFragment : ListFragment { val resultData = data.getParcelableExtra(ArticleDetailEntity::class.java.simpleName) historyEntity?.apply { - if (title != resultData?.title || brief != HtmlUtils.stripHtmlCode(resultData.content)) { - onLoadRefresh() - return - } - count.vote = resultData.count.vote - count.comment = resultData.count.comment - me.isCommunityArticleVote = resultData.me.isCommunityArticleVote + count.vote = resultData?.count?.vote ?: 0 + count.comment = resultData?.count?.comment ?: 0 + title = resultData?.title ?: "" + brief = HtmlUtils.stripHtmlCode(resultData?.content ?: "") + me.isCommunityArticleVote = resultData?.me?.isCommunityArticleVote ?: false } } 102 -> { val resultData = data.getParcelableExtra(AnswerDetailEntity::class.java.simpleName) historyEntity?.apply { - if (brief != HtmlUtils.stripHtmlCode(resultData?.content ?: "")) { - onLoadRefresh() - return - } count.vote = resultData?.vote ?: 0 count.comment = resultData?.commentCount ?: 0 + brief = HtmlUtils.stripHtmlCode(resultData?.content ?: "") question.title = resultData?.question?.title ?: "" } } 103 -> { val resultData = data.getParcelableExtra(QuestionsDetailEntity::class.java.simpleName) historyEntity?.apply { - if (title != resultData?.title ?: "" || description != resultData?.description ?: "") { - onLoadRefresh() - return - } - count.answer = resultData?.answersCount ?: 0 + title = resultData?.title ?: "" + count.answer = resultData?.count?.answer ?: count.answer + } + } + 104 -> { + val resultData = data.getParcelableExtra(ForumVideoEntity::class.java.simpleName) + historyEntity?.apply { + count.vote = resultData?.count?.vote ?: 0 + count.comment = resultData?.count?.comment ?: 0 + des = HtmlUtils.stripHtmlCode(resultData?.des ?: "") + title = resultData?.title ?: "" + me.isVoted = resultData?.me?.isVoted ?: false } } } diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryViewModel.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryViewModel.kt index 243d951da1..2e2096c239 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryViewModel.kt @@ -23,13 +23,8 @@ class UserHistoryViewModel(application: Application, var userId: String, private private val mApi = RetrofitManager.getInstance(getApplication()).api - var type: TYPE = TYPE.ALL - init { - setOverLimitSize(1) - } - override fun provideDataObservable(page: Int): Observable> { return mApi.getPersonalHistory(userId, page, HaloApp.getInstance().channel, getFilter()) @@ -112,13 +107,15 @@ class UserHistoryViewModel(application: Application, var userId: String, private ALL("all"), COMMUNITY_ARTICLE("community_article"), QUESTION("question"), - ANSWER("answer"); + ANSWER("answer"), + VIDEO("video"); fun getDisplayText() = when (this) { ALL -> "全部" QUESTION -> "提问" ANSWER -> "回答" COMMUNITY_ARTICLE -> "帖子" + VIDEO -> "视频" } companion object { diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryAdapter.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryAdapter.kt similarity index 92% rename from app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryAdapter.kt rename to app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryAdapter.kt index 45d48f518f..4fb0aa94a0 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryAdapter.kt @@ -1,4 +1,4 @@ -package com.gh.gamecenter.personalhome.home +package com.gh.gamecenter.personalhome.home.game import android.content.Context import android.util.SparseBooleanArray @@ -51,8 +51,10 @@ class UserCommentHistoryAdapter(context: Context, override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is UserCommentHistoryItemViewHolder -> { - var padTop = if (position == 0) 0F.dip2px() else 16F.dip2px() - holder.binding.gameInfo.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 0) + val marginTop = if (position == 0) 8F.dip2px() else 16F.dip2px() + holder.binding.gameInfo.layoutParams = (holder.binding.gameInfo.layoutParams as ViewGroup.MarginLayoutParams).apply { + setMargins(0, marginTop, 0, 0) + } var isChildLongClick = false val rating = mEntityList[position] @@ -60,6 +62,9 @@ class UserCommentHistoryAdapter(context: Context, holder.binding.divider.visibility = if (position == 0) View.GONE else View.VISIBLE + holder.binding.runTimeTv.text = NumberUtils.transSimpleUsageTime(rating.game.playedTime) + holder.binding.date.text = TimeUtils.getFormatTime(rating.time, "") + val maxDesLines = if (mExpandSparseBooleanArray.get(holder.adapterPosition)) Int.MAX_VALUE else 3 holder.binding.tvComment.setExpandMaxLines(maxDesLines) holder.binding.tvComment.setIsExpanded(Int.MAX_VALUE == maxDesLines) diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryFragment.kt similarity index 55% rename from app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryFragment.kt rename to app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryFragment.kt index a2f1d6ddc6..1dabf37cfb 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryFragment.kt @@ -1,36 +1,30 @@ -package com.gh.gamecenter.personalhome.home +package com.gh.gamecenter.personalhome.home.game import android.os.Bundle import android.view.View -import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.os.bundleOf -import butterknife.OnClick import com.gh.common.util.* import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment -import com.gh.gamecenter.databinding.FragmentUserCommentHistoryBinding +import com.gh.gamecenter.personalhome.home.UserHistoryFragment +import com.gh.gamecenter.personalhome.rating.MyRating import com.halo.assistant.HaloApp -class UserCommentHistoryFragment : ListFragment() { +class UserCommentHistoryFragment : ListFragment() { private var mUserId: String = "" private var mAdapter: UserCommentHistoryAdapter? = null - private var mBinding: FragmentUserCommentHistoryBinding? = null private lateinit var mViewModel: UserCommentHistoryViewModel - override fun getLayoutId() = 0 - - override fun getInflatedLayout() = FragmentUserCommentHistoryBinding.inflate(layoutInflater).apply { mBinding = this }.root - override fun onCreate(savedInstanceState: Bundle?) { arguments?.apply { mUserId = getString(USER_ID, "") } super.onCreate(savedInstanceState) - + mListRv.overScrollMode = View.OVER_SCROLL_NEVER } override fun getItemDecoration() = null @@ -54,10 +48,6 @@ class UserCommentHistoryFragment : ListFragment { - mViewModel.changeType(UserCommentHistoryViewModel.TYPE.ALL) - updateTypeBg(0) - } - - R.id.excellentType -> { - mViewModel.changeType(UserCommentHistoryViewModel.TYPE.JINGXUAN) - updateTypeBg(1) - } - - R.id.anliType -> { - mViewModel.changeType(UserCommentHistoryViewModel.TYPE.ANLIWALL) - updateTypeBg(2) - } - } - } - - private fun updateTypeBg(index: Int) { - mBinding?.run { - for (i in 0 until typeContainer.childCount) { - val sizeTv = typeContainer.getChildAt(i) as TextView - if (i == index) { - sizeTv.setTextColor(R.color.theme_font.toColor()) - sizeTv.setBackgroundResource(R.drawable.button_round_ebf5ff) - } else { - sizeTv.setTextColor(R.color.text_333333.toColor()) - sizeTv.setBackgroundResource(R.drawable.button_round_f5f5f5) - } - } + fun changeType(text: String) { + when (text) { + "全部" -> mViewModel.changeType(UserCommentHistoryViewModel.TYPE.ALL) + "精华" -> mViewModel.changeType(UserCommentHistoryViewModel.TYPE.JINGXUAN) + "安利墙" -> mViewModel.changeType(UserCommentHistoryViewModel.TYPE.ANLIWALL) } } diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryViewModel.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryViewModel.kt similarity index 98% rename from app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryViewModel.kt rename to app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryViewModel.kt index c28e3a3c21..e41de14e90 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserCommentHistoryViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserCommentHistoryViewModel.kt @@ -1,4 +1,4 @@ -package com.gh.gamecenter.personalhome.home +package com.gh.gamecenter.personalhome.home.game import android.app.Application import androidx.lifecycle.ViewModel diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameFragment.kt new file mode 100644 index 0000000000..2d12705a54 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameFragment.kt @@ -0,0 +1,169 @@ +package com.gh.gamecenter.personalhome.home.game + +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import android.widget.PopupWindow +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.os.bundleOf +import com.gh.common.util.* +import com.gh.common.util.EntranceUtils.KEY_COMMENT_COUNT +import com.gh.common.util.EntranceUtils.KEY_USER_ID +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentUserGameBinding +import com.gh.gamecenter.normal.NormalFragment + +class UserGameFragment: NormalFragment() { + + private var mBinding: FragmentUserGameBinding? = null + private var mViewModel: UserGameViewModel? = null + private var mPlayedGameFragment: UserPlayedGameFragment? = null + private var mCommentFragment: UserCommentHistoryFragment? = null + private var mUserId: String = "" + private var mFilter: String = "全部" + private var mCommentCount = 0 + private var mCurrentType = TYPE_PLAYED_GAME + + override fun getLayoutId() = 0 + + override fun getInflatedLayout() = FragmentUserGameBinding.inflate(layoutInflater).apply { mBinding = this }.root + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mUserId = arguments?.getString(KEY_USER_ID, "") ?: "" + mCommentCount = arguments?.getInt(KEY_COMMENT_COUNT, 0) ?: 0 + mViewModel = viewModelProvider(UserGameViewModel.Factory(mUserId)) + changeType(TYPE_PLAYED_GAME) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mViewModel?.playedGameCount?.observeNonNull(viewLifecycleOwner) { + mBinding?.gameType?.text = "玩过 $it" + } + + mBinding?.run { + commentType.text = "评论 $mCommentCount" + + gameType.setOnClickListener { + if (mCurrentType == TYPE_PLAYED_GAME) return@setOnClickListener + mCurrentType = TYPE_PLAYED_GAME + updateTypeView(TYPE_PLAYED_GAME) + changeType(TYPE_PLAYED_GAME) + } + + commentType.setOnClickListener { + if (mCurrentType == TYPE_COMMENT) return@setOnClickListener + mCurrentType = TYPE_COMMENT + updateTypeView(TYPE_COMMENT) + changeType(TYPE_COMMENT) + } + + commentSubType.setOnClickListener { + showCommentTypePopupWindow() + } + } + } + + private fun updateTypeView(type: Int) { + mBinding?.run { + if (type == TYPE_PLAYED_GAME) { + commentSubType.visibility = View.GONE + arrowIv.visibility = View.GONE + gameType.setBackgroundResource(R.drawable.button_round_f0f8ff) + gameType.setTextColor(R.color.theme_font.toColor()) + commentType.setBackgroundResource(R.drawable.button_round_fafafa) + commentType.setTextColor(R.color.text_333333.toColor()) + } else { + commentSubType.visibility = View.VISIBLE + arrowIv.visibility = View.VISIBLE + gameType.setBackgroundResource(R.drawable.button_round_fafafa) + gameType.setTextColor(R.color.text_333333.toColor()) + commentType.setBackgroundResource(R.drawable.button_round_f0f8ff) + commentType.setTextColor(R.color.theme_font.toColor()) + } + } + } + + private fun changeType(type: Int) { + if (type == TYPE_PLAYED_GAME) { + mPlayedGameFragment = childFragmentManager.findFragmentByTag(UserPlayedGameFragment::class.java.simpleName) as? UserPlayedGameFragment + ?: UserPlayedGameFragment() + mPlayedGameFragment?.arguments = bundleOf(KEY_USER_ID to mUserId) + childFragmentManager.beginTransaction().replace(R.id.contentContainer, mPlayedGameFragment!!, UserPlayedGameFragment::class.java.simpleName).commitAllowingStateLoss() + } else { + mCommentFragment = childFragmentManager.findFragmentByTag(UserCommentHistoryFragment::class.java.simpleName) as? UserCommentHistoryFragment + ?: UserCommentHistoryFragment() + mCommentFragment?.arguments = bundleOf(KEY_USER_ID to mUserId) + childFragmentManager.beginTransaction().replace(R.id.contentContainer, mCommentFragment!!, UserCommentHistoryFragment::class.java.simpleName).commitAllowingStateLoss() + } + } + + private fun showCommentTypePopupWindow() { + mBinding?.run { + val contentList = arrayListOf("全部", "精华", "安利墙") + + val inflater = LayoutInflater.from(requireContext()) + val layout = inflater.inflate(R.layout.layout_popup_container, null) + val popupWindow = PopupWindow( + layout, + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + popupWindow.apply { + setBackgroundDrawable(ColorDrawable(0)) + isTouchable = true + isFocusable = true + isOutsideTouchable = true + } + + val container = layout.findViewById(R.id.container) + for (text in contentList) { + val item = inflater.inflate(R.layout.layout_popup_option_item, container, false) + if (mFilter == text) { + (item as TextView).setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font)) + } else { + (item as TextView).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_666666)) + } + container.addView(item) + + val hitText = item.findViewById(R.id.hint_text) + hitText.text = text + + item.setOnClickListener { + mFilter = text + commentSubType.text = text + changeCommentType(text) + popupWindow.dismiss() + } + } + popupWindow.setOnDismissListener { + arrowIv.rotation = 0f + } + arrowIv.rotation = 180f + popupWindow.showAutoOrientation(commentSubType) + } + } + + private fun changeCommentType(text: String) { + mCommentFragment?.changeType(text) + } + + companion object { + private const val TYPE_PLAYED_GAME = 100 + private const val TYPE_COMMENT = 101 + + fun getInstance(userId: String, commentCount: Int): UserGameFragment { + return UserGameFragment().apply { + with(bundleOf( + KEY_USER_ID to userId, + KEY_COMMENT_COUNT to commentCount + )) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameViewModel.kt new file mode 100644 index 0000000000..d587b49d51 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserGameViewModel.kt @@ -0,0 +1,47 @@ +package com.gh.gamecenter.personalhome.home.game + +import android.annotation.SuppressLint +import android.app.Application +import android.text.TextUtils +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import com.lightgame.utils.Utils +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody + +class UserGameViewModel(application: Application, private val mUserId: String) + : AndroidViewModel(application) { + + var playedGameCount = MutableLiveData() + + init { + getUserPlayedGameCount() + } + + @SuppressLint("CheckResult") + fun getUserPlayedGameCount() { + RetrofitManager.getInstance(getApplication()) + .api.getPlayedGamesCount(mUserId, Utils.getTime(getApplication())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: retrofit2.Response) { + val countContent = data.headers().get("Total") + playedGameCount.postValue(if (TextUtils.isEmpty(countContent)) 0 else countContent?.toInt() ?: 0) + } + }) + } + + + class Factory(private val userId: String) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return UserGameViewModel(HaloApp.getInstance().application, userId) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameAdapter.kt new file mode 100644 index 0000000000..66bdcf91de --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameAdapter.kt @@ -0,0 +1,137 @@ +package com.gh.gamecenter.personalhome.home.game + +import android.content.Context +import android.util.TypedValue +import android.view.ViewGroup +import android.widget.TextView +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.ItemViewType +import com.gh.common.util.* +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.game.GameItemViewHolder +import com.gh.gamecenter.mygame.PlayedGameViewModel +import com.lightgame.download.DownloadEntity +import java.util.HashMap + +class UserPlayedGameAdapter(context: Context, + private val mViewModel: PlayedGameViewModel): ListAdapter(context) { + + private val mEntrance = "个人主页-游戏-玩过" + + val positionAndPackageMap = HashMap() + + override fun setListData(updateData: MutableList?) { + // 记录游戏位置 + if (updateData != null) { + for (i in 0 until updateData.size) { + val gameEntity = updateData[i] + var packages = gameEntity.id + for (apkEntity in gameEntity.getApk()) { + packages += apkEntity.packageName + } + positionAndPackageMap[packages + i] = i + } + } + super.setListData(updateData) + } + + override fun getItemCount(): Int { + return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1 + } + + override fun getItemViewType(position: Int): Int { + return if (position == itemCount - 1) { + ItemViewType.ITEM_FOOTER + } else { + ItemViewType.GAME_NORMAL + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.GAME_NORMAL -> GameItemViewHolder(DataBindingUtil.inflate(mLayoutInflater, R.layout.game_item, parent, false)) + + else -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is GameItemViewHolder -> { + holder.binding.run { + root.setPadding(16F.dip2px(), 8F.dip2px(), 16F.dip2px(), 8F.dip2px()) + + val gameEntity = mEntityList[position] + game = gameEntity + executePendingBindings() + + val platform = PlatformUtils.getInstance(mContext).getPlatformName(gameEntity.platform) + gameDes.run { + setTextSize(TypedValue.COMPLEX_UNIT_SP, 11F) + setTextColor(R.color.text_666666.toColor()) + text = platform + } + + labelList.removeAllViews() + val runTimeView = TextView(mContext).apply { + isSingleLine = true + setTextSize(TypedValue.COMPLEX_UNIT_SP, 11F) + setTextColor(R.color.text_999999.toColor()) + text = ("游戏时长: " + NumberUtils.transSimpleUsageTime(gameEntity.playedTime)) + } + labelList.addView(runTimeView) + + DownloadItemUtils.setOnClickListener(mContext, + downloadBtn, + gameEntity, + position, + this@UserPlayedGameAdapter, + "(${mEntrance})", + StringUtils.buildString(mEntrance, ":", gameEntity.name)) + DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding), true, true) + + holder.itemView.setOnClickListener { + GameDetailActivity.startGameDetailActivity(mContext, gameEntity.id, "(${mEntrance})") + } + } + } + + is FooterViewHolder -> holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + } + } + fun notifyItemByDownload(download: DownloadEntity) { + for (key in positionAndPackageMap.keys) { + // sentry上报download.packageName可能为空 + if (download.packageName != null + && download.gameId != null + && key.contains(download.packageName) + && key.contains(download.gameId)) { + val position = positionAndPackageMap[key] + if (position != null && mEntityList != null && position < mEntityList.size) { + mEntityList[position].getEntryMap()[download.platform] = download + notifyItemChanged(position) + } + } + } + } + + fun notifyItemAndRemoveDownload(status: EBDownloadStatus) { + for (key in positionAndPackageMap.keys) { + if (key.contains(status.packageName) && key.contains(status.gameId)) { + val position = positionAndPackageMap[key] + if (position != null && mEntityList != null && position < mEntityList.size) { + mEntityList[position].getEntryMap().remove(status.platform) + notifyItemChanged(position) + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameFragment.kt new file mode 100644 index 0000000000..70d8602043 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/game/UserPlayedGameFragment.kt @@ -0,0 +1,117 @@ +package com.gh.gamecenter.personalhome.home.game + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.Constants +import com.gh.common.util.DialogUtils +import com.gh.common.util.EntranceUtils.KEY_USER_ID +import com.gh.common.util.SPUtils +import com.gh.common.util.viewModelProvider +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus +import com.gh.download.DownloadManager +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.entity.GameInstall +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.manager.PackagesManager +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.mygame.PlayedGameViewModel +import com.gh.gamecenter.personalhome.InstalledGameDialog +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +class UserPlayedGameFragment: ListFragment() { + + private var mUserId = "" + private var mAdapter: UserPlayedGameAdapter? = null + private var mDialog: InstalledGameDialog? = null + private val mDataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + mAdapter?.notifyItemByDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } + } + } + + override fun provideListViewModel() = viewModelProvider(PlayedGameViewModel.Factory(mUserId)) + + override fun provideListAdapter() = mAdapter ?: UserPlayedGameAdapter(requireContext(), mListViewModel).apply { mAdapter = this } + + override fun getItemDecoration() = null + + override fun onCreate(savedInstanceState: Bundle?) { + mUserId = arguments?.getString(KEY_USER_ID, "") ?: "" + super.onCreate(savedInstanceState) + mListRv.overScrollMode = View.OVER_SCROLL_NEVER + } + + override fun onResume() { + if (isEverPause && mAdapter != null) mAdapter?.notifyDataSetChanged() + super.onResume() + DownloadManager.getInstance(context).addObserver(mDataWatcher) + } + + + override fun onLoadEmpty() { + super.onLoadEmpty() + + val installedList = PackagesManager.filterSameApk(PackagesManager.filterDownloadBlackPackage(PackagesManager.getInstalledList())) + val simulatorDownloadEntityList = DownloadManager.getInstance(requireContext()).allSimulatorDownloadEntity + simulatorDownloadEntityList.forEach { entity -> + installedList.add(GameInstall(entity.gameId, packageName = entity.packageName, name = entity.name, icon = entity.icon)) + } + //val count = SPUtils.getInt(Constants.SP_MARK_INSTALLED_GAME, 0) + val isCancel = SPUtils.getBoolean(Constants.SP_MARK_INSTALLED_GAME_USER_HOME, false) + if (mUserId == UserManager.getInstance().userId + && !isCancel +// && it.isNullOrEmpty() + && installedList.isNotEmpty()) { + mDialog = InstalledGameDialog(requireContext(), installedList, "个人主页详情", "标记玩过弹窗") + mDialog?.show() + mDialog?.onConfirmClickListener = { + onLoadRefresh() + } + } + } + + override fun onPause() { + super.onPause() + DownloadManager.getInstance(context).removeObserver(mDataWatcher) + } + + // 下载被删除事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBDownloadStatus) { + if ("delete" == status.status) { + mAdapter?.notifyItemAndRemoveDownload(status) + } + } + + // 安装/卸载 事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(busFour: EBPackage) { + if ("安装" == busFour.type || "卸载" == busFour.type) { + mAdapter?.notifyDataSetChanged() + } + } + + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = mAdapter?.positionAndPackageMap ?: return + for (gameAndPosition in data) { + if (gameAndPosition.key.contains(downloadEntity.packageName)) { + val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value) + if (targetView != null) { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/rating/MyRating.kt b/app/src/main/java/com/gh/gamecenter/personalhome/rating/MyRating.kt index ce2e182be6..e6a44dff00 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/rating/MyRating.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/rating/MyRating.kt @@ -3,6 +3,8 @@ package com.gh.gamecenter.personalhome.rating import androidx.annotation.Keep import com.gh.gamecenter.entity.* import com.google.gson.annotations.SerializedName +import java.text.SimpleDateFormat +import java.util.* /** * 该实体与View同级,因为其他页面也用不着 @@ -38,6 +40,12 @@ data class MyRating(@SerializedName("_id") user = user) } + fun getFormatTime(pattern: String): String? { + if (time == 0L) return null + val formatTime = SimpleDateFormat(pattern, Locale.CHINA) + return formatTime.format(time * 1000) + } + fun transformGameEntity(): GameEntity { return GameEntity(id = game.id, mName = game.name, nameSuffix = game.nameSuffix, mIcon = game.icon, mTagStyle = game.tag, star = game.star) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/BbsType.kt b/app/src/main/java/com/gh/gamecenter/qa/BbsType.kt new file mode 100644 index 0000000000..82c46b65c4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/BbsType.kt @@ -0,0 +1,11 @@ +package com.gh.gamecenter.qa + +enum class BbsType(val value: String) { + GAME_BBS("game_bbs"),//游戏论坛 + OFFICIAL_BBS("official_bbs"),//官方论坛 + + GAME_BBS_QUESTION_INSERT("game_bbs_question_insert"),//游戏论坛提问视频插入 + GAME_BBS_ARTICLE_INSERT("game_bbs_article_insert"),//游戏论坛帖子视频插入 + OFFICIAL_BBS_QUESTION_INSERT("official_bbs_question_insert"),//官方论坛提问视频插入 + OFFICIAL_BBS_ARTICLE_INSERT("official_bbs_article_insert")//官方论坛帖子视频插入 +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/CommunityFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/CommunityFragment.kt index c337e74242..c85a6f0137 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/CommunityFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/CommunityFragment.kt @@ -390,7 +390,7 @@ class CommunityFragment : BaseLazyTabFragment() { @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(busNine: EBUISwitch) { if (MainWrapperFragment.EB_MAIN_SCROLL_TOP == busNine.from - && MainWrapperFragment.INDEX_ASK == busNine.position) { + && MainWrapperFragment.INDEX_BBS == busNine.position) { when (mTabLayout.getTabAt(mViewPager.currentItem)?.text) { TAB_TITLE_FOLLOW -> mAskFollowFragment?.scrollToTop(false) TAB_TITLE_RECOMMEND -> mRecommendsFragment?.scrollToTop(false) diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt index 5018ba0bac..1927e89809 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt @@ -1,25 +1,32 @@ package com.gh.gamecenter.qa.answer +import android.annotation.SuppressLint import android.view.View import android.widget.TextView import com.airbnb.lottie.LottieAnimationView import com.gh.base.BaseRecyclerViewHolder import com.gh.common.util.* +import com.gh.common.view.GameIconView import com.gh.gamecenter.R import com.gh.gamecenter.entity.CommunityEntity import com.gh.gamecenter.entity.VoteEntity import com.gh.gamecenter.forum.detail.ForumDetailActivity import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity -import com.gh.gamecenter.qa.comment.CommentActivity import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.ArticleEntity +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity +import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity +import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.lightgame.utils.Utils import com.lightgame.view.CheckableImageView import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody import retrofit2.HttpException /** @@ -34,6 +41,9 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH val commentCountContainer: View = itemView.findViewById(R.id.comment_count_container) val voteCountContainer: View = itemView.findViewById(R.id.vote_count_container) val forumNameTv: View = itemView.findViewById(R.id.forumNameTv) + val forumNameLl: View? = itemView.findViewById(R.id.forumNameLl) + val forumNameContainer: View? = itemView.findViewById(R.id.forumNameContainer) + val forumIcon: GameIconView? = itemView.findViewById(R.id.forumIcon) open fun bindCommendAndVote(entity: AnswerEntity, entrance: String) { @@ -41,20 +51,24 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH commentCountContainer.setOnClickListener { if (filterIllegalCommentStatus(entity.commentable, entity.active)) return@setOnClickListener - if (entity.type == "community_article") { - val communityId = if (entity.articleCommunityId.isNotEmpty()) entity.articleCommunityId - else UserManager.getInstance().community.id - val intent = ArticleDetailActivity.getCommentIntent(itemView.context, - CommunityEntity(communityId, entity.communityName ?: ""), - entity.id ?: "", - entrance, "") - itemView.context.startActivity(intent) - } else { - val intent = CommentActivity.getAnswerCommentIntent(itemView.context, - entity.id!!, - entity.commentCount, - false) - itemView.context.startActivity(intent) + when (entity.type) { + "community_article" -> { + val communityId = if (!entity.communityId.isNullOrEmpty()) entity.communityId + ?: "" else entity.bbs.id + val intent = ArticleDetailActivity.getCommentIntent(itemView.context, + CommunityEntity(communityId, entity.communityName ?: ""), + entity.id ?: "", entrance, "") + itemView.context.startActivity(intent) + } + "video" -> { + itemView.context.startActivity(ForumVideoDetailActivity.getIntent(itemView.context, entity.id + ?: "")) + } + else -> { + val intent = NewQuestionDetailActivity.getCommentIntent(itemView.context, entity.id + ?: "", entrance, "") + itemView.context.startActivity(intent) + } } } @@ -93,23 +107,38 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH open fun bindCommendAndVote(entity: ArticleEntity, entrance: String) { binNormalView(entity.transformAnswerEntity()) + if (entity.bbs.type == "official_bbs") { + forumIcon?.displayGameIcon(entity.bbs.icon, null) + } else { + forumIcon?.displayGameIcon(entity.bbs.game?.getIcon(), entity.bbs.game?.iconSubscript) + } + forumNameTv.setOnClickListener { MtaHelper.onEvent(getEventId(entrance), getKey(entrance), if (entity.bbs.name.isEmpty()) entity.community.name else entity.bbs.name) itemView.context.startActivity(ForumDetailActivity.getIntent(itemView.context, entity.community.id, entrance)) LogUtils.uploadAccessToBbs(entity.community.id, "文章外所属论坛") } + forumNameContainer?.setOnClickListener { + forumNameTv.performClick() + } + commentCountContainer.setOnClickListener { if (filterIllegalCommentStatus(entity.commentable, entity.active)) return@setOnClickListener - val communityId = if (entity.community.id.isNotEmpty()) entity.community.id - else UserManager.getInstance().community.id - val intent = ArticleDetailActivity.getCommentIntent(itemView.context, - CommunityEntity(communityId, entity.community.name), - entity.id, - entrance, "") - itemView.context.startActivity(intent) - MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "评论图标") + if (entity.type == "question") { + val intent = NewQuestionDetailActivity.getCommentIntent(it.context, entity.id, entrance, "") + itemView.context.startActivity(intent) + } else { + val communityId = if (entity.community.id.isNotEmpty()) entity.community.id + else UserManager.getInstance().community.id + val intent = ArticleDetailActivity.getCommentIntent(itemView.context, + CommunityEntity(communityId, entity.community.name), + entity.id, + entrance, "") + itemView.context.startActivity(intent) + MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "评论图标") + } } voteCountContainer.setOnClickListener { @@ -117,9 +146,20 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH debounceActionWithInterval(R.id.container_like, 1000) { CheckLoginUtils.checkLogin(itemView.context, entrance) { - if (!voteIcon.isChecked) voteArticle(entity) - else cancelArticleVote(entity) - MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "点赞图标") + if (entity.type == "question") { + val questionsDetailEntity = QuestionsDetailEntity( + id = entity.id, + title = entity.title, + images = entity.images, + description = entity.brief) + it.context.startActivity(QuestionsInviteActivity.getIntent(it.context, + questionsDetailEntity, + entrance)) + } else { + if (!voteIcon.isChecked) voteArticle(entity) + else cancelArticleVote(entity) + MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "点赞图标") + } } } } @@ -127,23 +167,31 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH fun binNormalView(entity: AnswerEntity) { setVoteAndCommentStyle(entity) - commentCount.text = if (entity.commentCount > 0) entity.commentCount.toString() else "评论" - voteCount.text = if (entity.vote > 0) entity.vote.toString() else "赞同" + if (entity.type == "question") { + commentCount.text = if (entity.count.answer > 0) entity.count.answer.toString() else "回答" + voteCount.text = "邀请回答" + } else { + commentCount.text = if (entity.count.comment > 0) entity.count.comment.toString() else "评论" + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + } } private fun setVoteAndCommentStyle(entity: AnswerEntity) { - - if (!entity.active) { - voteIcon.setImageResource(R.drawable.community_vote_unavailable) - voteCount.setTextColor(R.color.text_cccccc.toColor()) - } else if (entity.me.isCommunityArticleVote || entity.me.isAnswerVoted) { - voteIcon.setImageResource(R.drawable.community_vote_selector) - voteIcon.isChecked = true - voteCount.setTextColor(R.color.theme_font.toColor()) + if (entity.type == "question") { + voteIcon.setImageDrawable(R.drawable.community_invite_follow.toDrawable()) } else { - voteIcon.setImageResource(R.drawable.community_vote_selector) - voteIcon.isChecked = false - voteCount.setTextColor(R.color.text_999999.toColor()) + if (!entity.active) { + voteIcon.setImageResource(R.drawable.community_vote_unavailable) + voteCount.setTextColor(R.color.text_cccccc.toColor()) + } else if (entity.me.isCommunityArticleVote || entity.me.isAnswerVoted || entity.me.isVoted) { + voteIcon.setImageResource(R.drawable.community_vote_selector) + voteIcon.isChecked = true + voteCount.setTextColor(R.color.theme_font.toColor()) + } else { + voteIcon.setImageResource(R.drawable.community_vote_selector) + voteIcon.isChecked = false + voteCount.setTextColor(R.color.text_999999.toColor()) + } } if (entity.commentable && entity.active) { @@ -159,119 +207,160 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH } } + @SuppressLint("CheckResult") fun voteAnswer(entity: AnswerEntity) { - entity.vote = entity.vote + 1 - voteCount.text = if (entity.vote > 0) entity.vote.toString() else "赞同" + entity.count.vote = entity.count.vote + 1 + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" playVoteAnimation() - val voteObservable = if (entity.type == "community_article") { - val communityId = if (entity.articleCommunityId.isNotEmpty()) entity.articleCommunityId - else entity.bbs.id - RetrofitManager.getInstance(itemView.context).api - .postCommunityArticleVote(communityId, entity.id) - } else { - RetrofitManager.getInstance(itemView.context).api - .postAnswerVote(entity.id) - } - voteObservable - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - override fun onResponse(response: VoteEntity?) { - if (entity.type == "community_article") { - entity.me.isCommunityArticleVote = true - EnergyTaskHelper.postEnergyTask("vote_community_article", entity.id) - } else { - entity.me.isAnswerVoted = true - EnergyTaskHelper.postEnergyTask("vote_answer", entity.id) + if (entity.type == "video") { + RetrofitManager.getInstance(itemView.context) + .api.voteVideo(entity.id) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + //Utils.toast(getApplication(), "已点赞") + EnergyTaskHelper.postEnergyTask("vote_video", entity.id) } - ToastUtils.showToast("已赞同") - } - override fun onFailure(e: HttpException?) { - ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { - if (403008 == it) { - Utils.toast(itemView.context, R.string.ask_vote_hint) - true - } else if (403036 == it) { - Utils.toast(itemView.context, R.string.ask_vote_limit_hint) - true - } else if (404001 == it) { - Utils.toast(itemView.context, "内容可能已被删除") - entity.active = false - setVoteAndCommentStyle(entity) - true + override fun onFailure(exception: Exception) { + super.onFailure(exception) + entity.count.vote = entity.count.vote - 1 + entity.me.isVoted = false + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity) + } + }) + } else { + val voteObservable = if (entity.type == "community_article") { + val communityId = if (entity.articleCommunityId.isNotEmpty()) entity.articleCommunityId + else entity.bbs.id + RetrofitManager.getInstance(itemView.context).api + .postCommunityArticleVote(communityId, entity.id) + } else { + RetrofitManager.getInstance(itemView.context).api + .postAnswerVote(entity.id) + } + voteObservable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: VoteEntity?) { + if (entity.type == "community_article") { + entity.me.isCommunityArticleVote = true + EnergyTaskHelper.postEnergyTask("vote_community_article", entity.id) } else { - entity.vote = entity.vote - 1 - if (entity.type == "community_article") { - entity.me.isCommunityArticleVote = true + entity.me.isAnswerVoted = true + EnergyTaskHelper.postEnergyTask("vote_answer", entity.id) + } + ToastUtils.showToast("已赞同") + } + + override fun onFailure(e: HttpException?) { + ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { + if (403008 == it) { + Utils.toast(itemView.context, R.string.ask_vote_hint) + true + } else if (403036 == it) { + Utils.toast(itemView.context, R.string.ask_vote_limit_hint) + true + } else if (404001 == it) { + Utils.toast(itemView.context, "内容可能已被删除") + entity.active = false + setVoteAndCommentStyle(entity) + true } else { - entity.me.isAnswerVoted = true + entity.count.vote = entity.count.vote - 1 + if (entity.type == "community_article") { + entity.me.isCommunityArticleVote = true + } else { + entity.me.isAnswerVoted = true + } + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity) + false } - voteCount.text = if (entity.vote > 0) entity.vote.toString() else "赞同" - setVoteAndCommentStyle(entity) - false } } - } - }) + }) + } } + @SuppressLint("CheckResult") fun cancelAnswerVote(entity: AnswerEntity) { - entity.vote = entity.vote - 1 + entity.count.vote = entity.count.vote - 1 voteIcon.isChecked = false voteCount.setTextColor(R.color.text_999999.toColor()) - voteCount.text = if (entity.vote > 0) entity.vote.toString() else "赞同" + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - val unVoteObservable = if (entity.type == "community_article") { - entity.me.isCommunityArticleVote = false + if (entity.type == "video") { + RetrofitManager.getInstance(itemView.context) + .api.undoVoteVideo(entity.id) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + Utils.toast(itemView.context, "取消点赞") + } - val communityId = if (entity.articleCommunityId.isNotEmpty()) entity.articleCommunityId - else entity.bbs.id - RetrofitManager.getInstance(itemView.context).api - .postCommunityArticleUnVote(communityId, entity.id) + override fun onFailure(exception: Exception) { + super.onFailure(exception) + entity.count.vote = entity.count.vote + 1 + entity.me.isVoted = true + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity) + } + }) } else { - entity.me.isAnswerVoted = false + val unVoteObservable = if (entity.type == "community_article") { + entity.me.isCommunityArticleVote = false - RetrofitManager.getInstance(itemView.context).api - .postAnswerUnvote(entity.id) - } - unVoteObservable - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - override fun onResponse(response: VoteEntity?) { - Utils.toast(itemView.context, "取消赞同") - } + val communityId = if (entity.articleCommunityId.isNotEmpty()) entity.articleCommunityId + else entity.bbs.id + RetrofitManager.getInstance(itemView.context).api + .postCommunityArticleUnVote(communityId, entity.id) + } else { + entity.me.isAnswerVoted = false - override fun onFailure(e: HttpException?) { - ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { - if (403008 == it) { - Utils.toast(itemView.context, R.string.ask_vote_hint) - true - } else if (403036 == it) { - Utils.toast(itemView.context, R.string.ask_vote_limit_hint) - true - } else if (404001 == it) { - Utils.toast(itemView.context, "内容可能已被删除") - entity.active = false - setVoteAndCommentStyle(entity) - true - } else { - entity.vote = entity.vote + 1 - if (entity.type == "community_article") { - entity.me.isCommunityArticleVote = true + RetrofitManager.getInstance(itemView.context).api + .postAnswerUnvote(entity.id) + } + unVoteObservable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: VoteEntity?) { + Utils.toast(itemView.context, "取消赞同") + } + + override fun onFailure(e: HttpException?) { + ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { + if (403008 == it) { + Utils.toast(itemView.context, R.string.ask_vote_hint) + true + } else if (403036 == it) { + Utils.toast(itemView.context, R.string.ask_vote_limit_hint) + true + } else if (404001 == it) { + Utils.toast(itemView.context, "内容可能已被删除") + entity.active = false + setVoteAndCommentStyle(entity) + true } else { - entity.me.isAnswerVoted = true + entity.count.vote = entity.count.vote + 1 + if (entity.type == "community_article") { + entity.me.isCommunityArticleVote = true + } else { + entity.me.isAnswerVoted = true + } + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity) + false } - voteCount.text = if (entity.vote > 0) entity.vote.toString() else "赞同" - setVoteAndCommentStyle(entity) - false } } - } - }) + }) + } } fun voteArticle(entity: ArticleEntity) { @@ -360,6 +449,24 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH }) } + fun followUser(entity: AnswerEntity, callback: EmptyCallback?) { + RetrofitManager.getInstance(itemView.context).api + .postFollowing(entity.user.id) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + callback?.onCallback() + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + Utils.toast(itemView.context, R.string.loading_failed_hint) + } + }) + } + private fun playVoteAnimation() { voteCount.setTextColor(R.color.theme_font.toColor()) voteIcon.isChecked = true diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/CommunityAnswerItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/CommunityAnswerItemViewHolder.kt index 22e78b26fe..20b9b4c7a2 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/CommunityAnswerItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/CommunityAnswerItemViewHolder.kt @@ -68,13 +68,19 @@ class CommunityAnswerItemViewHolder(val binding: CommunityAnswerItemBinding) : B binding.userIcon.display(entity.user.border, entity.user.icon, entity.user.auth?.icon) binding.imageContainer.bindData(entity, entrance, path) - if (entity.getPassVideos().isNotEmpty() && entity.images.isNotEmpty()) { - val title = binding.title.text - val videoSpan = SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build() - binding.title.text = SpannableStringBuilder() - .append(title) - .append(videoSpan) + val spanBuilder = SpannableStringBuilder() + if (entity.type == "question") { + val title = " ${binding.title.text}" + val askLabelSpan = SpanBuilder(title).image(0, 1, R.drawable.ic_ask_label).build() + spanBuilder.append(askLabelSpan) + } else { + spanBuilder.append(binding.title.text) } + if (entity.getPassVideos().isNotEmpty() && entity.images.isNotEmpty()) { + val videoSpan = SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build() + spanBuilder.append(videoSpan) + } + binding.title.text = spanBuilder val user = entity.user binding.userBadgeName.setOnClickListener { binding.userBadgeIcon.performClick() } binding.userBadgeIcon.setOnClickListener { diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt index c206e5ea71..bc7d9f012b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt @@ -63,7 +63,7 @@ import java.io.IOException import java.util.* import kotlin.collections.ArrayList -class AnswerDetailFragment : NormalFragment() { +open class AnswerDetailFragment : NormalFragment() { @BindView(R.id.reuse_no_connection) lateinit var mNoConn: View @@ -126,7 +126,7 @@ class AnswerDetailFragment : NormalFragment() { private var mRefreshFooter: AnswerDetailRefreshFooter? = null private var mSkeletonScreen: SkeletonScreen? = null - private lateinit var mBinding: FragmentAnswerDetailBinding + lateinit var mBinding: FragmentAnswerDetailBinding lateinit var mViewModel: AnswerDetailViewModel private lateinit var mContainerViewModel: AnswerDetailContainerViewModel @@ -994,10 +994,14 @@ class AnswerDetailFragment : NormalFragment() { private fun hideLoadingViewAndShowContent() { mNoConn.visibility = View.GONE - mBinding.bottomController.root.visibility = View.VISIBLE + mBinding.bottomController.root.visibility = if (shouldShowBottomController()) View.VISIBLE else View.GONE mBinding.scrollView.visibility = View.VISIBLE } + open fun shouldShowBottomController(): Boolean { + return true + } + @SuppressLint("SetTextI18n") private fun updateQuestionView(question: Questions) { val video = if (!question.videos.isNullOrEmpty()) question.videos[0] else null @@ -1062,7 +1066,7 @@ class AnswerDetailFragment : NormalFragment() { mBinding.answerCountTv.text = String.format("查看全部%d个回答", question.answerCount) // 作为 viewpager 里的第一个回答时隐藏问题内容 - if (mAnswerId == mContainerViewModel.answerIdList.first()) { + if (mContainerViewModel.answerIdList.isNotEmpty() && mAnswerId == mContainerViewModel.answerIdList.first()) { mQuestionContent.visibility = View.VISIBLE } else { mQuestionContent.visibility = View.GONE diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/SimpleAnswerDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/SimpleAnswerDetailActivity.kt new file mode 100644 index 0000000000..71a9234155 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/SimpleAnswerDetailActivity.kt @@ -0,0 +1,21 @@ +package com.gh.gamecenter.qa.answer.detail + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.base.BaseActivity +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity + +class SimpleAnswerDetailActivity : NormalActivity() { + + companion object { + fun getIntent(context: Context, answerId: String, entrance: String, path: String): Intent { + val bundle = Bundle() + bundle.putString(EntranceUtils.KEY_ANSWER_ID, answerId) + bundle.putString(EntranceUtils.KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path)) + bundle.putString(EntranceUtils.KEY_PATH, path) + return getTargetIntent(context, SimpleAnswerDetailActivity::class.java, SimpleAnswerDetailFragment::class.java, bundle) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/SimpleAnswerDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/SimpleAnswerDetailFragment.kt new file mode 100644 index 0000000000..aad62b30cb --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/SimpleAnswerDetailFragment.kt @@ -0,0 +1,19 @@ +package com.gh.gamecenter.qa.answer.detail + +import android.os.Bundle +import android.view.View + +class SimpleAnswerDetailFragment : AnswerDetailFragment() { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mBinding.refreshLayout.isEnabled = false + mBinding.answerDetailTitleRl.visibility = View.GONE + mBinding.bottomController.root.visibility = View.GONE + mBinding.topPaddingView.visibility = View.GONE + mBinding.bottomShadowView.visibility = View.GONE + mBinding.bottomDividerView.visibility = View.GONE + } + + override fun shouldShowBottomController(): Boolean = false +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt index 1a7c83e9bb..21c21357ab 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt @@ -31,32 +31,20 @@ import com.gh.gamecenter.qa.entity.Questions import com.gh.gamecenter.video.VideoVerifyItemViewHolder import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel import com.halo.assistant.HaloApp -import com.zhihu.matisse.Matisse -import com.zhihu.matisse.MimeType -import org.json.JSONArray import org.json.JSONObject /** * Created by khy on 10/04/18. */ -class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { +class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { private lateinit var mMenuDraft: MenuItem private lateinit var mMenuPost: MenuItem private lateinit var mBinding: FragmentAnswerEditBinding - private lateinit var mViewModel: AnswerEditViewModel - private var mProcessingDialog: WaitingDialogFragment? = null private var mUploadImageCancelDialog: Dialog? = null - private var mCommunityName: String? = null - private var mOpenAnswerInNewPage: Boolean = false - private var mAgreePostPic: Boolean = false - - private var mKeyboardHeightProvider: KeyboardHeightProvider? = null - - private var mIsKeyBoardShow = false override fun mtaEventName(): String { return "回答详情" // issues 定的就是回答详情 @@ -72,9 +60,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - if (requestCode == REQUEST_CODE_IMAGE && resultCode == Activity.RESULT_OK) { - if (data != null) mViewModel.postImg(data) - } else if (requestCode == ANSWER_DRAFT_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + if (requestCode == ANSWER_DRAFT_REQUEST_CODE && resultCode == Activity.RESULT_OK) { mViewModel.getUserAnswerDrafts() } } @@ -87,15 +73,6 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mCommunityName = intent?.getStringExtra(EntranceUtils.KEY_COMMUNITY_NAME) mOpenAnswerInNewPage = intent?.getBooleanExtra(EntranceUtils.KEY_ANSWER_OPEN_IN_NEW_PAGE, false)!! mBinding = FragmentAnswerEditBinding.bind(mContentView) - - val factory = AnswerEditViewModel.Factory( - HaloApp.getInstance().application, - intent?.getStringExtra(EntranceUtils.KEY_ANSWER_ID), - intent?.getStringExtra(EntranceUtils.KEY_ANSWER_CONTENT), - intent?.getStringExtra(EntranceUtils.KEY_DRAFT_ID), - intent?.getParcelableExtra(Questions::class.java.simpleName) ?: Questions()) - mViewModel = ViewModelProviders.of(this, factory).get(AnswerEditViewModel::class.java) - addObserver() initQuestionContent() @@ -133,28 +110,27 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mMenuDraft.isVisible = false } - mKeyboardHeightProvider = KeyboardHeightProvider(this) - mBinding.root.post { mKeyboardHeightProvider?.start() } checkPostButtonEnable() } + override fun provideViewModel(): AnswerEditViewModel { + val factory = AnswerEditViewModel.Factory( + HaloApp.getInstance().application, + intent?.getStringExtra(EntranceUtils.KEY_ANSWER_ID), + intent?.getStringExtra(EntranceUtils.KEY_ANSWER_CONTENT), + intent?.getStringExtra(EntranceUtils.KEY_DRAFT_ID), + intent?.getParcelableExtra(Questions::class.java.simpleName) ?: Questions()) + mViewModel = ViewModelProviders.of(this, factory).get(AnswerEditViewModel::class.java) + return mViewModel + } + override fun getLayoutId(): Int { return R.layout.fragment_answer_edit } override fun onKeyboardHeightChanged(height: Int, orientation: Int) { + super.onKeyboardHeightChanged(height, orientation) if (height > 0) mBinding.appBar.setExpanded(false, true) - mIsKeyBoardShow = height > 0 - } - - override fun onResume() { - super.onResume() - mKeyboardHeightProvider?.setKeyboardHeightObserver(this) - } - - override fun onPause() { - super.onPause() - mKeyboardHeightProvider?.setKeyboardHeightObserver(null) } override fun onMenuItemClick(item: MenuItem?): Boolean { @@ -170,7 +146,9 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mViewModel.postLiveData.observeNonNull(this) { if (it.status == Status.SUCCESS) { MtaHelper.onEvent("发表答案", "提交成功", mCommunityName) - toast("发布成功") + if (mViewModel.checkIsAllUploadedAndToast()) { + toast("发布成功") + } var answerId: String? = null tryWithDefaultCatch { answerId = JSONObject(it.data).getString("_id") @@ -200,19 +178,9 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { when (code) { 403037 -> consume { if (TextUtils.isEmpty(mViewModel.draftId)) { - DialogUtils.showAlertDialog(this, - "发布失败", - "问题已被删除,无法发布回答", - "好吧", - "", - { finish() }, - null) + DialogUtils.showAlertDialog(this, "发布失败", "问题已被删除,无法发布回答", "好吧", "", { finish() }, null) } else { - DialogUtils.showAlertDialog(this, - "发布失败", - "问题已被删除,需要删除草稿吗?", - "删除草稿", - "暂不", + DialogUtils.showAlertDialog(this, "发布失败", "问题已被删除,需要删除草稿吗?", "删除草稿", "暂不", { mViewModel.deleteAnswerDraft() finish() @@ -250,23 +218,6 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { } } - mViewModel.chooseImagesUpload.observe(this, Observer { - 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()) - }) - mViewModel.deleteDraftLiveDate.observe(this, Observer { if (it == true) { setResult(Activity.RESULT_OK) @@ -445,8 +396,6 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mProcessingDialog?.dismissAllowingStateLoss() mProcessingDialog = null super.onDestroy() - - mKeyboardHeightProvider?.close() } private fun showDraftFailureDialog() { @@ -481,28 +430,11 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mViewModel.saveAnswerDrafts(editContent, forcedExit) } - @OnClick( - R.id.editor_image, - R.id.question_images_1, - R.id.question_images_2, - R.id.question_images_3) + @OnClick(R.id.question_images_1, R.id.question_images_2, R.id.question_images_3) fun onClick(view: View) { val videoSize = if (!mViewModel.question.videos.isNullOrEmpty()) 1 else 0 when (view.id) { - R.id.editor_image -> { - MtaHelper.onEvent("发表答案", "上传图片", mCommunityName) - if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) { - mAgreePostPic = true - DialogUtils.showAlertDialog(this, - "警告", - "当前使用移动网络,上传图片会消耗手机流量", - "我知道了", "", { startMediaStore() }, null) - MtaHelper.onEvent("发表答案", "上传图片-移动网络提示", mCommunityName) - return - } - startMediaStore() - } R.id.question_images_1 -> { val question = mViewModel.question if (!question.videos.isNullOrEmpty()) { @@ -538,46 +470,17 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { } } - - private fun startMediaStore() { - MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片") - if (mViewModel.mapImages.size >= 50) { - toast(R.string.answer_edit_max_img_hint) - return - } - try { - PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback { - override fun onCallback() { - Matisse.from(this@AnswerEditActivity) - .choose(MimeType.ofImage()) - .showSingleMediaType(true) - .countable(true) - .addFilter(GhMatisseFilter()) - .maxSelectable(10) - .forResult(REQUEST_CODE_IMAGE) - } - }) - } catch (e: Exception) { - toast(R.string.media_image_hint) - e.printStackTrace() - } - } - companion object { - const val SAVE_DRAFTS = 110 const val AUTO_SAVE_DRAFT = 1 - const val REQUEST_CODE_IMAGE = 111 const val SAVE_DRAFTS_INTERVAL_TIME = 15000 const val SAVE_DRAFTS_TOAST_COUNT = 3 const val MIN_ANSWER_TEXT_LENGTH = 6 const val MAX_ANSWER_TEXT_LENGTH = 10000 const val ANSWER_DRAFT_REQUEST_CODE = 112 - const val FILE_HOST = "file:///" const val ANSWER_DRAFT_CHANGE_TAG = "ANSWER_DRAFT_CHANGE_TAG" - /** * 撰写回答 */ diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditViewModel.kt index 04c358c816..0f43a17765 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditViewModel.kt @@ -1,7 +1,6 @@ package com.gh.gamecenter.qa.answer.edit import android.app.Application -import android.content.Intent import android.text.TextUtils import androidx.lifecycle.* import com.gh.base.fragment.WaitingDialogFragment @@ -13,16 +12,14 @@ import com.gh.gamecenter.R import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mvvm.Resource +import com.gh.base.BaseRichEditorViewModel +import com.gh.base.RichType import com.gh.gamecenter.qa.entity.AnswerDraftEntity import com.gh.gamecenter.qa.entity.Questions import com.gh.gamecenter.retrofit.Response -import com.gh.gamecenter.retrofit.RetrofitManager import com.lightgame.utils.Utils -import com.zhihu.matisse.Matisse -import com.zhihu.matisse.internal.utils.PathUtils import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import okhttp3.MediaType import okhttp3.RequestBody @@ -32,34 +29,19 @@ import org.greenrobot.eventbus.EventBus import org.json.JSONException import org.json.JSONObject import retrofit2.HttpException -import java.io.File -import java.util.* class AnswerEditViewModel(application: Application, var answerId: String?, // 以mAnswerId为标识,如果mAnswerId不为空则是-修改答案(不需要保存草稿) 为空则是-编写答案 var answerContent: String?, var draftId: String?, - var question: Questions) : AndroidViewModel(application) { - + var question: Questions) : BaseRichEditorViewModel(application) { private var mPostDraftsCount: Int = 0 - private val mApi = RetrofitManager.getInstance(getApplication()).api - var cacheAnswerContent: String? = null - - val processDialog = MediatorLiveData() val postLiveData = MediatorLiveData>() - val postImageLiveData = MediatorLiveData>>() val deleteDraftLiveDate = MediatorLiveData() val draftsLiveData = MediatorLiveData() val saveDraftsLiveData = MediatorLiveData() // 自动保存不会回调 - val uploadingImage = ArrayList>() - val chooseImagesUpload = MutableLiveData>() - val chooseImagesUploadSuccess = MutableLiveData>() - - var uploadImageSubscription: Disposable? = null - val mapImages = HashMap() - fun postAnswer(editContent: String) { processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) @@ -141,80 +123,6 @@ class AnswerEditViewModel(application: Application, }) } - fun postImg(data: Intent) { - val uris = Matisse.obtainResult(data) - val pictureList = ArrayList() - for (uri in uris) { - val picturePath = PathUtils.getPath(getApplication(), uri) - if (picturePath != null) { - if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) { - val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024 - val application: Application = getApplication() - Utils.toast(getApplication(), application.getString(R.string.pic_max_hint, count)) - continue - } - Utils.log("picturePath = $picturePath") - pictureList.add(picturePath) - } else { - Utils.log("picturePath is null") - } - } - - if (pictureList.size == 0) return - uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(UploadImageUtils.UploadType.answer, pictureList - , false, object : UploadImageUtils.OnUploadImageListListener { - override fun onProgress(total: Long, progress: Long) { - } - - override fun onCompressSuccess(imageUrls: List) { - val chooseImageMd5Map = LinkedHashMap() - imageUrls.forEach { - chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = "" - } - uploadingImage.add(chooseImageMd5Map) - chooseImagesUpload.postValue(chooseImageMd5Map) - - } - override fun onSuccess(imageUrl: LinkedHashMap, errorMap: Map) { - val uploadMap = uploadingImage.find { it.containsKey(MD5Utils.getUrlMD5(imageUrl.entries.iterator().next().key)) } - uploadMap?.let { - for (key in imageUrl.keys) { - uploadMap[MD5Utils.getUrlMD5(key)] = AnswerEditActivity.FILE_HOST + key.decodeURI() - mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: "" - } - chooseImagesUploadSuccess.postValue(uploadMap) - uploadingImage.remove(uploadMap) - } - val errorSize = pictureList.size - imageUrl.size - if (errorSize > 0) { - for (error in errorMap.values) { - if (error is HttpException && error.code() == 403) { - Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败") - return - } - } - Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败") - } - } - - override fun onError(errorMap: Map) { - val errorSize = pictureList.size - - for (error in errorMap.values) { - if (error is HttpException && error.code() == 403) { - Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败") - return - } - } - if (errorSize == 1) { - Utils.toast(getApplication(), "图片上传失败") - } else { - Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败") - } - } - }) - } - fun getUserAnswerDrafts() { mApi .getUserAnswerDrafts(question.id, @@ -260,7 +168,9 @@ class AnswerEditViewModel(application: Application, super.onResponse(response) if (forcedExit) { saveDraftsLiveData.postValue(true) - Utils.toast(getApplication(), "回答已保存到草稿箱") + if (checkIsAllUploadedAndToast()) { + Utils.toast(getApplication(), "回答已保存到草稿箱") + } EventBus.getDefault().post(EBReuse(AnswerEditActivity.ANSWER_DRAFT_CHANGE_TAG)) } else { if (mPostDraftsCount >= AnswerEditActivity.SAVE_DRAFTS_TOAST_COUNT) { @@ -280,6 +190,8 @@ class AnswerEditViewModel(application: Application, }) } + override fun getRichType(): RichType = RichType.ANSWER + class Factory(private val mApplication: Application, private val answerId: String?, private val answerContent: String?, diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleAdapter.kt index 424dcb01ab..bbdb554cb2 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleAdapter.kt @@ -4,17 +4,24 @@ import android.content.Context import android.text.SpannableStringBuilder import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.ItemViewType import com.gh.common.syncpage.ISyncAdapterHandler import com.gh.common.util.SpanBuilder +import com.gh.common.util.toDrawable import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.CommunityMyAnswerItemBinding import com.gh.gamecenter.qa.answer.MyAnswerViewHolder import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity +import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.ArticleEntity +import com.gh.gamecenter.qa.entity.Questions +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity +import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity class MyArticleAdapter(context: Context, private val mEntrance: String, @@ -51,15 +58,26 @@ class MyArticleAdapter(context: Context, holder.bindItem(entity, mEntrance) holder.binding.executePendingBindings() holder.binding.imageContainer.bindData(entity.transformAnswerEntity(), mEntrance, mPath) - if (entity.getPassVideos().isNotEmpty() && entity.images.isNotEmpty()) { - val title = holder.binding.title.text - val videoSpan = SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build() - holder.binding.title.text = SpannableStringBuilder() - .append(title) - .append(videoSpan) + val spanBuilder = SpannableStringBuilder() + if (entity.type == "question") { + val title = " ${holder.binding.title.text}" + val askLabelSpan = SpanBuilder(title).image(0, 1, R.drawable.ic_ask_label).build() + spanBuilder.append(askLabelSpan) + } else { + spanBuilder.append(holder.binding.title.text) } + holder.binding.title.text = spanBuilder + if (entity.getPassVideos().isNotEmpty() && entity.images.isNotEmpty()) { + val videoSpan = SpanBuilder(" ").image(1, " ".length, R.drawable.ic_article_video_label).build() + spanBuilder.append(videoSpan) + } + holder.itemView.setOnClickListener { - mContext.startActivity(ArticleDetailActivity.getIntent(mContext, entity.community, entity.id, mEntrance, mPath)) + if (entity.type == "question") { + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, entity.id, mEntrance, mPath)) + } else { + mContext.startActivity(ArticleDetailActivity.getIntent(mContext, entity.bbs, entity.id, mEntrance, mPath)) + } } } else if (holder is FooterViewHolder) { holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver) diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleFragment.kt index 9605139395..4612a528e2 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/MyArticleFragment.kt @@ -14,10 +14,12 @@ import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.baselist.NormalListViewModel import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.article.edit.ArticleEditActivity +import com.gh.gamecenter.qa.entity.AnswerEntity import com.gh.gamecenter.qa.entity.ArticleEntity import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import io.reactivex.Observable +import io.reactivex.functions.Function class MyArticleFragment : ListFragment>() { @@ -37,7 +39,16 @@ class MyArticleFragment : ListFragment>? { - return RetrofitManager.getInstance(HaloApp.getInstance().application).api.getMyArticle(mTargetUserId, page) + return RetrofitManager.getInstance(HaloApp.getInstance().application).api.getMyArticleAndQuestion(mTargetUserId, page) + .flatMap(object :Function, Observable>>{ + override fun apply(list: List): Observable> { + return Observable.create { emitter-> + val articleList = list.map { it.transformArticleEntity() }.toMutableList() + emitter.onNext(articleList) + emitter.onComplete() + } + } + }) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailAdapter.kt index 62b97939d0..6cce9bea9b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailAdapter.kt @@ -7,9 +7,10 @@ import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.R import com.gh.gamecenter.baselist.LoadStatus import com.gh.gamecenter.databinding.ItemArticleDetailContentBinding +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter class ArticleDetailAdapter(context: Context, var mViewModel: ArticleDetailViewModel, type: AdapterType, entrance: String) - : BaseArticleDetailCommentAdapter(context, mViewModel, type, entrance) { + : BaseCommentAdapter(context, mViewModel, type, entrance) { var articleDetailVH: ArticleDetailContentViewHolder? = null @@ -27,7 +28,7 @@ class ArticleDetailAdapter(context: Context, var mViewModel: ArticleDetailViewMo override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { ITEM_ARTICLE_DETAIL -> { - val binding: ItemArticleDetailContentBinding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_article_detail_content, parent, false) + val binding: ItemArticleDetailContentBinding = ItemArticleDetailContentBinding.inflate(mLayoutInflater, parent, false) ArticleDetailContentViewHolder(binding, mViewModel).apply { articleDetailVH = this } } @@ -41,7 +42,7 @@ class ArticleDetailAdapter(context: Context, var mViewModel: ArticleDetailViewMo holder.bindView(mEntityList[position].articleDetail!!) } - is ArticleDetailCommentFilterViewHolder -> { + is CommentFilterViewHolder -> { holder.bindView(mViewModel.detailEntity) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailContentViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailContentViewHolder.kt index 43e3320093..9275b9da8e 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailContentViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailContentViewHolder.kt @@ -76,7 +76,8 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin } binding.run { - detail = article + userNameTv.text = article.user.name + titleTv.text = article.title userIconIv.display(article.user.border, article.user.icon, article.user.auth?.icon) richEditor.setContentOwner(article.me.isContentOwner) // 避免列表频繁刷新 @@ -90,11 +91,15 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin releaseTimeTv.text = String.format("发布于%s 最后编辑于%s", NewsUtils.getFormattedTime(article.time.create), NewsUtils.getFormattedTime(article.time.edit)) } richEditor.visibility = View.VISIBLE - - tagsFlexbox.removeAllViews() - addTag(article.community.name, true) - for (tag in article.tags) { - addTag(tag) + article.community.let { entity -> + gameName.text = entity.name + val icon = if (!entity.icon.isNullOrEmpty()) entity.icon else entity.game?.getIcon() + val iconSubscript = if (!entity.iconSubscript.isNullOrEmpty()) entity.iconSubscript else entity.game?.iconSubscript + forumIconView.displayGameIcon(icon, iconSubscript) + forumContainer.setOnClickListener { + DirectUtils.directForumDetail(forumContainer.context, entity.id, "帖子详情") + LogUtils.uploadAccessToBbs(entity.id, "文章内所属论坛") + } } followBtn.visibility = View.VISIBLE @@ -104,6 +109,10 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin followBtn.isEnabled = true updateFollowBtn(article.me.isFollower) } + if (article.tagActivityId.isNotEmpty() && article.tagActivityName.isNotEmpty()) { + activityNameTv.text = article.tagActivityName + activityNameTv.visibility = View.VISIBLE + } badgeIv.goneIf(article.user.badge == null) badgeTv.goneIf(article.user.badge == null) @@ -136,45 +145,6 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin } } - private fun addTag(tag: String, isCommunityName: Boolean = false) { - binding.run { - if (isCommunityName) { - val binding = ArticleDetailFourmTagItemBinding.bind(LayoutInflater.from(root.context).inflate(R.layout.article_detail_fourm_tag_item, null)) - binding.tagTv.text = tag - - binding.root.setOnClickListener { - MtaHelper.onEvent("帖子详情", "内容区域", tag) - DirectUtils.directForumDetail(binding.tagTv.context, viewModel.detailEntity?.communityId, "帖子详情") - LogUtils.uploadAccessToBbs(viewModel.detailEntity?.communityId, "文章内所属论坛") - } - tagsFlexbox.addView(binding.root) - - val params = binding.root.layoutParams as FlexboxLayout.LayoutParams - params.setMargins(0, 5F.dip2px(), 8F.dip2px(), 5F.dip2px()) - binding.root.layoutParams = params - } else { - val view = LayoutInflater.from(root.context).inflate(R.layout.questionsdedit_tag_item, null) - val tagTv = view as TextView - - tagTv.text = tag - tagTv.setTextColor(R.color.theme_font.toColor()) - tagTv.setPadding(14F.dip2px(), 0, 14F.dip2px(), 0) - tagTv.setRoundedColorBackground(R.color.text_EEF5FB, 5F) - tagTv.setOnClickListener { - MtaHelper.onEvent("帖子详情", "内容区域", tag) - MtaHelper.onEvent("问题标签", viewModel.detailEntity?.community?.name, viewModel.detailEntity!!.title + "-" + tag) - root.context.startActivity(AskColumnDetailActivity.getIntentByTag(root.context, tag, - CommunityEntity(viewModel.detailEntity!!.communityId), mEntrance, "帖子详情")) - } - tagsFlexbox.addView(view) - - val params = view.layoutParams as FlexboxLayout.LayoutParams - params.setMargins(0, 5F.dip2px(), 8F.dip2px(), 5F.dip2px()) - tagTv.layoutParams = params - } - - } - } inner class JsInterface { @JavascriptInterface diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt index 8fc3245569..0c65e71510 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt @@ -5,6 +5,7 @@ import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.LayoutInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.ImageView @@ -15,7 +16,6 @@ import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.RecyclerView -import butterknife.BindView import com.ethanhua.skeleton.Skeleton import com.gh.common.TimeElapsedHelper import com.gh.common.history.HistoryHelper @@ -28,6 +28,7 @@ import com.gh.gamecenter.R import com.gh.gamecenter.SuggestionActivity import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.databinding.FragmentArticleDetailBinding import com.gh.gamecenter.entity.* import com.gh.gamecenter.eventbus.EBDeleteCommentDetail import com.gh.gamecenter.eventbus.EBDeleteDetail @@ -36,35 +37,38 @@ import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.personal.PersonalFragment import com.gh.gamecenter.qa.article.edit.ArticleEditActivity import com.gh.gamecenter.qa.comment.CommentActivity +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter +import com.gh.gamecenter.qa.comment.base.BaseCommentFragment +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog import com.gh.gamecenter.qa.entity.ArticleDetailEntity import com.gh.gamecenter.qa.entity.ArticleDraftEntity import com.gh.gamecenter.suggest.SuggestType import com.halo.assistant.HaloApp -import kotlinx.android.synthetic.main.fragment_article_detail.* -import kotlinx.android.synthetic.main.piece_article_input_container.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* import kotlin.collections.ArrayList -class ArticleDetailFragment : BaseArticleDetailCommentFragment() { - - @BindView(R.id.reuse_tv_none_data) - lateinit var mNoDataText: TextView - +class ArticleDetailFragment : BaseCommentFragment() { private var mElapsedHelper: TimeElapsedHelper? = null - private var mScrollToCommentArea: Boolean = false private var mIsRecommendsContent: Boolean = false - private var mSpecialColumn: SpecialColumn? = null - private var mAdapter: ArticleDetailAdapter? = null + private var mAttentionMenu: MenuItem? = null private lateinit var mViewModel: ArticleDetailViewModel + private lateinit var mBinding: FragmentArticleDetailBinding + private var mIsToolbarUserShow = false - override fun getLayoutId() = R.layout.fragment_article_detail + override fun getLayoutId() = 0 + + override fun getInflatedLayout(): View { + return FragmentArticleDetailBinding.inflate(LayoutInflater.from(requireContext()), null, false).apply { + mBinding = this + }.root + } override fun onCreate(savedInstanceState: Bundle?) { mViewModel = provideListViewModel() @@ -119,7 +123,7 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment { return mAdapter - ?: ArticleDetailAdapter(requireContext(), mViewModel, BaseArticleDetailCommentAdapter.AdapterType.COMMENT, mEntrance).apply { mAdapter = this } + ?: ArticleDetailAdapter(requireContext(), mViewModel, BaseCommentAdapter.AdapterType.COMMENT, mEntrance).apply { mAdapter = this } } override fun onBackPressed(): Boolean { @@ -176,16 +180,16 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment - (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop + ViewCompat.setOnApplyWindowInsetsListener(mBinding.toolbar) { _, insets -> + (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop insets.consumeSystemWindowInsets() } mSkeletonScreen = Skeleton.bind(skeletonView).shimmer(false).load(R.layout.fragment_article_detail_skeleton).show() - bottomLikeIv.setOnClickListener { + mBinding.inputContainer.bottomLikeIv.setOnClickListener { MtaHelper.onEvent("帖子详情", "底部", "点赞") requireContext().ifLogin("帖子详情-赞同") { if (mViewModel.detailEntity?.me?.isCommunityArticleVote == false) { @@ -198,7 +202,7 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment { + BaseCommentViewModel.LoadResult.SUCCESS -> { updateView() // GdtHelper.logAction(ActionType.PAGE_VIEW, // GdtHelper.CONTENT_TYPE, "QA_ARTICLE", // GdtHelper.CONTENT_ID, mViewModel.articleId) } else -> { - if (it == BaseArticleDetailCommentViewModel.LoadResult.DELETED) { + if (it == BaseCommentViewModel.LoadResult.DELETED) { HistoryHelper.deleteArticleEntity(mViewModel.articleId) @@ -258,7 +262,7 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment(R.id.followBtn) + followBtn.setOnClickListener { mAdapter?.articleDetailVH?.binding?.followBtn?.performClick() } + if (isFollow) { + followBtn.text = "已关注" + followBtn.setRoundedColorBackground(R.color.text_f5f5f5, 999F) + followBtn.setTextColor(R.color.text_999999.toColor()) + } else { + followBtn.text = "关注" + followBtn.setRoundedColorBackground(R.color.text_EEF5FB, 999F) + followBtn.setTextColor(R.color.theme_font.toColor()) + } + } } private fun showMoreItemDialog() { @@ -376,7 +403,7 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment 60F.dip2px()) { - forumGameIv.visibility = View.VISIBLE - ImageUtils.display(forumGameIv.getIconIv(), articleDetail.community.game?.getIcon()) - ImageUtils.display(forumGameIv.getIconDecoratorIv(), articleDetail.community.game?.iconSubscript) - - forumTitleTv.text = articleDetail.community.name - } else if (forumGameIv.visibility == View.VISIBLE - && mListRv.computeVerticalScrollOffset() <= 60F.dip2px()) { - forumGameIv.visibility = View.GONE - forumTitleTv.text = "" + if (!mIsToolbarUserShow && mListRv.computeVerticalScrollOffset() > 56F.dip2px()) { + mBinding.forumGameIv.visibility = View.GONE + mBinding.userAvatar.visibility = View.VISIBLE + mAttentionMenu?.isVisible = articleDetail.user.id != UserManager.getInstance().userId + mBinding.forumTitleTv.text = articleDetail.user.name + mIsToolbarUserShow = true + } else if (mIsToolbarUserShow && mListRv.computeVerticalScrollOffset() <= 56F.dip2px()) { + mBinding.forumGameIv.visibility = View.VISIBLE + mBinding.userAvatar.visibility = View.GONE + mAttentionMenu?.isVisible = false + mBinding.forumTitleTv.text = articleDetail.community.name + mIsToolbarUserShow = false } } }) @@ -494,26 +531,26 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment() + val mFollowLiveData = MutableLiveData() val hide = MutableLiveData() val like = MutableLiveData() @@ -67,6 +63,8 @@ class ArticleDetailViewModel(application: Application, commentCount = response?.count?.comment ?: 0 loadResultLiveData.postValue(LoadResult.SUCCESS) mergeListData(mListLiveData.value, displayFloor = true) + + NewLogUtils.logForumContentBrowser(articleId, "bbs_article") } override fun onFailure(e: HttpException?) { @@ -177,6 +175,15 @@ class ArticleDetailViewModel(application: Application, } } + private fun syncFollowData(isFollow: Boolean) { + articleId.apply { + SyncPageRepository.postSyncData(SyncDataEntity(this, + SyncFieldConstants.IS_FOLLOWER, + isFollow, + checkFieldEntity = true)) + } + } + fun collectionCommand(isCollection: Boolean, callback: (isFollow: Boolean) -> Unit) { val observable = if (isCollection) { mApi.postCommunityArticleFavorites(UserManager.getInstance().userId, communityId, articleId) @@ -233,9 +240,11 @@ class ArticleDetailViewModel(application: Application, if (isFollow) { // 关注成功 mFollowLiveData.postValue(true) + syncFollowData(true) } else { // 取消关注成功 mFollowLiveData.postValue(false) + syncFollowData(false) } } @@ -280,7 +289,7 @@ class ArticleDetailViewModel(application: Application, }) } - override fun hideCommunityArticleCommentSuccess() { + override fun hideCommentSuccess() { detailEntity?.count?.comment = (detailEntity?.count?.comment ?: 0) - 1 loadResultLiveData.postValue(LoadResult.SUCCESS) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/CommentItemData.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/CommentItemData.kt index 8ade53e6d8..b53106b0c8 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/CommentItemData.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/CommentItemData.kt @@ -2,9 +2,11 @@ package com.gh.gamecenter.qa.article.detail import com.gh.gamecenter.entity.CommentEntity import com.gh.gamecenter.qa.entity.ArticleDetailEntity +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity data class CommentItemData( val articleDetail: ArticleDetailEntity? = null, + val questionDetail: QuestionsDetailEntity? = null, var commentTop: CommentEntity? = null, var commentNormal: CommentEntity? = null, var filter: Boolean? = null, diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentAdapter.kt index e59907f961..287e2831da 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentAdapter.kt @@ -11,14 +11,15 @@ import com.gh.common.util.dip2px import com.gh.gamecenter.R import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding import com.gh.gamecenter.entity.CommentEntity -import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentAdapter +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter +@Deprecated("v5.0.0废弃") class ArticleDetailCommentAdapter(context: Context, var mViewModel: ArticleDetailCommentViewModel, type: AdapterType, private var mEntrance: String, commentClosure: ((CommentEntity) -> Unit)? = null) - : BaseArticleDetailCommentAdapter(context, mViewModel, type, mEntrance, commentClosure) { + : BaseCommentAdapter(context, mViewModel, type, mEntrance, commentClosure) { var topCommentVH: ArticleDetailTopCommentViewHolder? = null @@ -39,7 +40,7 @@ class ArticleDetailCommentAdapter(context: Context, holder.bindView(mEntityList[position].commentTop!!) } - is ArticleDetailCommentFilterViewHolder -> { + is CommentFilterViewHolder -> { holder.bindView(comment = mViewModel.commentDetail) } @@ -71,7 +72,7 @@ class ArticleDetailCommentAdapter(context: Context, binding.contentTv.text = comment.content binding.contentTv.maxLines = Int.MAX_VALUE - ArticleDetailCommentViewHolder.bindComment(binding, mViewModel, comment, mEntrance, null) + CommentItemViewHolder.bindComment(binding, mViewModel, comment, mEntrance, null) } } diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentFragment.kt index 356058fe87..d3925de9f1 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentFragment.kt @@ -14,14 +14,15 @@ import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.databinding.FragmentArticleDetailCommentBinding import com.gh.gamecenter.entity.CommentEntity import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentAdapter -import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentFragment -import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentViewModel +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter +import com.gh.gamecenter.qa.comment.base.BaseCommentFragment +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel import com.gh.gamecenter.qa.article.detail.CommentItemData import com.gh.gamecenter.qa.comment.CommentActivity import com.halo.assistant.HaloApp -class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment() { +@Deprecated("v5.0.0废弃") +class ArticleDetailCommentFragment : BaseCommentFragment() { private var mAdapter: ArticleDetailCommentAdapter? = null private lateinit var mViewModel: ArticleDetailCommentViewModel @@ -70,7 +71,7 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment { return mAdapter - ?: ArticleDetailCommentAdapter(requireContext(), mViewModel, BaseArticleDetailCommentAdapter.AdapterType.SUB_COMMENT, mEntrance) { + ?: ArticleDetailCommentAdapter(requireContext(), mViewModel, BaseCommentAdapter.AdapterType.SUB_COMMENT, mEntrance) { if (it.user.id == UserManager.getInstance().userId) { toast("不能回复自己") } else { @@ -110,7 +111,7 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment { + BaseCommentViewModel.LoadResult.SUCCESS -> { mReuseNoConn?.visibility = View.GONE mListLoading?.visibility = View.GONE @@ -133,7 +134,7 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment { - if (it == BaseArticleDetailCommentViewModel.LoadResult.DELETED) { + if (it == BaseCommentViewModel.LoadResult.DELETED) { mReuseNoConn?.visibility = View.GONE mReuseNoData?.visibility = View.VISIBLE toast(R.string.content_delete_toast) diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt index 8a4e29d54e..cca385a491 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt @@ -5,7 +5,7 @@ import android.app.Application import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.gh.gamecenter.entity.CommentEntity -import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentViewModel +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel import com.gh.gamecenter.qa.article.detail.CommentItemData import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.RetrofitManager @@ -14,11 +14,12 @@ import io.reactivex.Single import io.reactivex.schedulers.Schedulers import retrofit2.HttpException +@Deprecated("v5.0.0废弃") class ArticleDetailCommentViewModel(application: Application, articleId: String = "", communityId: String = "", var commentId: String = "") - : BaseArticleDetailCommentViewModel(application, articleId, communityId) { + : BaseCommentViewModel(application, articleId, "", "", communityId) { var commentDetail: CommentEntity? = null var positionInOriginList = -1 diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/draft/ArticleDraftAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/article/draft/ArticleDraftAdapter.kt index 8ca82e2700..75380b4aae 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/draft/ArticleDraftAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/draft/ArticleDraftAdapter.kt @@ -14,9 +14,9 @@ import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.databinding.CommunityArticleDraftItemBinding import com.gh.gamecenter.qa.entity.ArticleDraftEntity -class ArticleDraftAdapter(context: Context - , private val deleteCallback: (ArticleDraftEntity) -> Unit - , private val selectCallback: (ArticleDraftEntity) -> Unit) : ListAdapter(context) { +class ArticleDraftAdapter(context: Context, + private val deleteCallback: (ArticleDraftEntity) -> Unit, + private val selectCallback: (ArticleDraftEntity) -> Unit) : ListAdapter(context) { override fun areItemsTheSame(oldItem: ArticleDraftEntity, newItem: ArticleDraftEntity): Boolean { return !TextUtils.isEmpty(oldItem.id) && oldItem.id == newItem.id diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt index 0568e0dcb6..c02e09da7e 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt @@ -20,48 +20,41 @@ import androidx.lifecycle.ViewModelProviders import butterknife.OnClick import com.gh.base.BaseRichEditorActivity import com.gh.base.fragment.WaitingDialogFragment -import com.gh.common.AppExecutor import com.gh.common.util.* import com.gh.common.view.GameIconView import com.gh.gamecenter.R +import com.gh.gamecenter.entity.ActivityLabelEntity import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity import com.gh.gamecenter.qa.article.draft.ArticleDraftActivity -import com.gh.gamecenter.qa.dialog.ChooseForumDialogFragment +import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment +import com.gh.gamecenter.qa.dialog.ChooseForumActivity +import com.gh.gamecenter.qa.editor.GameActivity import com.gh.gamecenter.qa.entity.ArticleDetailEntity import com.gh.gamecenter.qa.entity.ArticleDraftEntity import com.gh.gamecenter.qa.questions.edit.TagsSelectFragment -import com.lightgame.utils.Util_System_Keyboard +import com.gh.gamecenter.qa.video.publish.VideoPublishFragment import com.lightgame.utils.Utils -import com.zhihu.matisse.Matisse -import com.zhihu.matisse.MimeType import kotterknife.bindView import org.greenrobot.eventbus.EventBus -import org.json.JSONArray -import org.json.JSONObject -class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { +class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { private val mEditPlaceholder by bindView(R.id.article_placeholder) private val mForumContainerView by bindView(R.id.forum_container) + private val mActivityTitle by bindView(R.id.activityTitle) private val mForumIcon by bindView(R.id.forum_icon_view) private val mGameName by bindView(R.id.article_game_name) private val mEditTitle by bindView(R.id.article_edit_title) private lateinit var mMenuDraft: MenuItem private lateinit var mMenuPost: MenuItem - - private lateinit var mViewModel: ArticleEditViewModel - private var mProcessingDialog: WaitingDialogFragment? = null private var mUploadImageCancelDialog: Dialog? = null - private var mPostDraftsCount: Int = 0 - private var mAgreePostPic = false - private val FILE_HOST = "file:///" - private var mKeyboardHeightProvider: KeyboardHeightProvider? = null - private var mIsKeyBoardShow = false private var mArticleTagsSelectFragment: ArticleTagsSelectFragment? = null override fun mtaEventName(): String { @@ -75,8 +68,7 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { override fun handleMessage(msg: Message) { super.handleMessage(msg) if (msg.what == 1) { - if (mViewModel.mSelectCommunityData != null - && !TextUtils.isEmpty(mEditTitle.text.trim()) + if (!TextUtils.isEmpty(mEditTitle.text.trim()) && (!TextUtils.isEmpty(mRichEditor.text) || mRichEditor.html.contains("(ArticleDraftEntity::class.java.simpleName) + if (data == null || resultCode != Activity.RESULT_OK) return + if (requestCode == ARTICLE_DRAFT_REQUEST_CODE) { + val draftEntity = data.getParcelableExtra(ArticleDraftEntity::class.java.simpleName) if (draftEntity != null) { mViewModel.draftEntity = draftEntity setArticleDraft() } + } else if (requestCode == ChooseForumActivity.CHOOSE_FORUM_REQUEST) { + val community = data.getParcelableExtra(EntranceUtils.KEY_COMMUNITY_DATA) + mViewModel.type = community?.type ?: "" + mViewModel.mSelectCommunityData = community + if (mViewModel.type == BbsType.GAME_BBS.value) { + mViewModel.gameEntity = null + } + setGameName() + } else if (requestCode == VideoPublishFragment.REQUEST_GAME_CODE) { + val game = data.getParcelableExtra(GameEntity::class.java.simpleName) + if (game != null) { + mViewModel.gameEntity = game + setGameName() + } } } @@ -109,15 +114,8 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mMenuPost = mToolbar.menu.findItem(R.id.menu_answer_post) mToolbar.navigationIcon = null mMenuDraft.isVisible = true - mViewModel = ViewModelProviders.of(this).get(ArticleEditViewModel::class.java) - mViewModel.detailEntity = intent.getParcelableExtra(ArticleDetailEntity::class.java.simpleName) - mViewModel.draftEntity = intent.getParcelableExtra(ArticleDraftEntity::class.java.simpleName) - mViewModel.openArticleInNewPage = intent.getBooleanExtra(EntranceUtils.KEY_ARTICLE_OPEN_IN_NEW_PAGE, false) checkPostButtonEnable() - mKeyboardHeightProvider = KeyboardHeightProvider(this) - mEditTitle.post { mKeyboardHeightProvider?.start() } - mRichEditor.setOnTextChangeListener { t -> if (t.contains(" if (pair != null) { @@ -187,9 +172,13 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { val intent = Intent() intent.putExtra(ArticleDraftEntity::class.java.simpleName, mViewModel.draftEntity) setResult(Activity.RESULT_OK, intent) - Utils.toast(this, "已保存!") + if (mViewModel.checkIsAllUploadedAndToast()) { + Utils.toast(this, "已保存!") + } } else { - Utils.toast(this, "帖子已保存到草稿箱") + if (mViewModel.checkIsAllUploadedAndToast()) { + Utils.toast(this, "帖子已保存到草稿箱") + } } EventBus.getDefault().post(EBReuse(ARTICLE_DRAFT_CHANGE_TAG)) finish() @@ -250,44 +239,31 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mViewModel.error.observeNonNull(this) { ErrorHelper.handleError(this, it) } - mViewModel.selectedTagsChange.observe(this, Observer { - val selectedTags = mViewModel.selectedTags - if (selectedTags.isEmpty()) { - mAddLabelTv.text = "添加标签" - mLabelNumTv.visibility = View.GONE - if (mTagsContainer.visibility == View.GONE) { - mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.text_666666)) - mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(this, R.drawable.ic_add_label), null, null, null) - } else { - mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font)) - mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - } - } else { - mAddLabelTv.text = "标签" - mLabelNumTv.goneIf(mTagsContainer.visibility == View.VISIBLE) - mLabelNumTv.text = selectedTags.size.toString() - mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font)) - mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - } - }) - if (mViewModel.detailEntity != null) { // 修改文章 内容填充 - setNavigationTitle("修改帖子") - setPatchContent() - } else if (mViewModel.draftEntity != null) { - setNavigationTitle("发布帖子") - setArticleDraft() - mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong()) - } else {// 启动自动保存草稿 - setNavigationTitle("发布帖子") - mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong()) - mViewModel.mSelectCommunityData = intent.getParcelableExtra(CommunityEntity::class.java.simpleName) - if (mViewModel.mSelectCommunityData != null) { - setGameName() - mGameName.isEnabled = false - mGameName.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) + when { + mViewModel.detailEntity != null -> { // 修改文章 内容填充 + setNavigationTitle("修改帖子") + setPatchContent() + } + mViewModel.draftEntity != null -> { + setNavigationTitle("发布帖子") + setArticleDraft() + mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong()) + } + else -> {// 启动自动保存草稿 + setNavigationTitle("发布帖子") + mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong()) + mViewModel.mSelectCommunityData = intent.getParcelableExtra(CommunityEntity::class.java.simpleName) + mViewModel.type = intent?.getStringExtra(BbsType::class.java.simpleName) ?: "" + if (mViewModel.mSelectCommunityData != null) { + setGameName() + if (mViewModel.type == BbsType.GAME_BBS.value) { + mGameName.isEnabled = false + mGameName.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) + } + } + mEditTitle.requestFocus() } - mEditTitle.requestFocus() } mViewModel.notSelectForum.observe(this, Observer { if (it) showSelectGameDialog() @@ -296,63 +272,23 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { override fun getSelectedLabel(): Int = mViewModel.selectedTags.size - @OnClick(R.id.backBtn, R.id.article_game_name, R.id.editor_image) + @OnClick(R.id.backBtn, R.id.article_game_name, R.id.chooseActivityContainer) fun onClick(v: View) { when (v.id) { R.id.backBtn -> { onBackPressed() } - R.id.editor_image -> { - MtaHelper.onEvent("发表文章", "上传图片", UserManager.getInstance().community.name) - if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) { - mAgreePostPic = true - DialogUtils.showAlertDialog(this, - "警告", - "当前使用移动网络,上传图片会消耗手机流量", - "我知道了", "", - DialogUtils.ConfirmListener { startMediaStore() }, null) - MtaHelper.onEvent("发表文章", "上传图片-移动网络提示", UserManager.getInstance().community.name) - return - } - startMediaStore() - } R.id.article_game_name -> { MtaHelper.onEvent("发表文章", "指定游戏", UserManager.getInstance().community.name) showSelectGameDialog() } - } - } - - private fun showDraftFailureDialog() { - DialogUtils.showAlertDialog(this, - "提示", - "确定退出?已撰写的内容将会丢失", - "继续撰写", "退出", - null, DialogUtils.CancelListener { finish() }) - } - - private fun startMediaStore() { - MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片") - if (mViewModel.mapImg.size >= 50) { - toast(R.string.answer_edit_max_img_hint) - return - } - - try { - PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback { - override fun onCallback() { - Matisse.from(this@ArticleEditActivity) - .choose(MimeType.ofImage()) - .showSingleMediaType(true) - .countable(true) - .addFilter(GhMatisseFilter()) - .maxSelectable(10) - .forResult(REQUEST_CODE_IMAGE) + R.id.chooseActivityContainer -> { + ChooseActivityDialogFragment.show(this, mViewModel.selectActivityLabelEntity?.id) { + mViewModel.selectActivityLabelEntity = it + mActivityTitle.text = it.name + mActivityTitle.setTextColor(R.color.text_FA8500.toColor()) } - }) - } catch (e: Exception) { - toast(R.string.media_image_hint) - e.printStackTrace() + } } } @@ -363,6 +299,14 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mViewModel.mSelectCommunityData?.icon = mViewModel.draftEntity?.community?.game?.getIcon() mViewModel.mSelectCommunityData?.iconSubscript = mViewModel.draftEntity?.community?.game?.iconSubscript } + if (!mViewModel.draftEntity?.tagActivityId.isNullOrEmpty() && !mViewModel.draftEntity?.tagActivityName.isNullOrEmpty()) { + mViewModel.selectActivityLabelEntity = ActivityLabelEntity(mViewModel.draftEntity?.tagActivityId + ?: "", mViewModel.draftEntity?.tagActivityName ?: "") + mActivityTitle.text = mViewModel.draftEntity?.tagActivityName ?: "" + mActivityTitle.setTextColor(R.color.text_FA8500.toColor()) + } + mViewModel.type = mViewModel.draftEntity?.type ?: "" + mViewModel.gameEntity = mViewModel.gameEntity mEditTitle.setText(mViewModel.draftEntity?.title) mGameName.isEnabled = true setGameName() @@ -371,10 +315,18 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { private fun setPatchContent() { //编辑贴子可能存在草稿 + mViewModel.type = mViewModel.detailEntity?.type ?: "" mViewModel.draftEntity = mViewModel.detailEntity?.me?.articleDraft mViewModel.mSelectCommunityData = mViewModel.detailEntity?.community mViewModel.mSelectCommunityData?.icon = mViewModel.detailEntity?.community?.game?.getIcon() mViewModel.mSelectCommunityData?.iconSubscript = mViewModel.detailEntity?.community?.game?.iconSubscript + mViewModel.gameEntity = mViewModel.detailEntity?.gameEntity + if (!mViewModel.detailEntity?.tagActivityId.isNullOrEmpty() && !mViewModel.detailEntity?.tagActivityName.isNullOrEmpty()) { + mViewModel.selectActivityLabelEntity = ActivityLabelEntity(mViewModel.detailEntity?.tagActivityId + ?: "", mViewModel.detailEntity?.tagActivityName ?: "") + mActivityTitle.text = mViewModel.detailEntity?.tagActivityName ?: "" + mActivityTitle.setTextColor(R.color.text_FA8500.toColor()) + } setGameName() //编辑帖子草稿箱入口不存在 @@ -404,15 +356,14 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { private fun getReplaceRealContent(): String { var content = mRichEditor.html - for (s in mViewModel.mapImg.keys) { - content = content?.replace(FILE_HOST + s, mViewModel.mapImg[s].toString()) + for (s in mViewModel.mapImages.keys) { + content = content?.replace(FILE_HOST + s, mViewModel.mapImages[s].toString()) } return content } override fun handleBackPressed(): Boolean { if (super.handleBackPressed()) { - changeAddLabel(false) return true } @@ -491,20 +442,34 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { } private fun showSelectGameDialog() { - ChooseForumDialogFragment.show(this) { - mViewModel.mSelectCommunityData = it - setGameName() + if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + startActivityForResult(GameActivity.getIntent(this, GameActivity.SELECT_GAME_TITLE), VideoPublishFragment.REQUEST_GAME_CODE) + } else { + ChooseForumActivity.startChooseForumActivity(this) } } private fun setGameName() { if (mViewModel.mSelectCommunityData != null) { - mGameName.text = mViewModel.mSelectCommunityData?.name - mForumIcon.visibility = View.VISIBLE - mForumIcon.displayGameIcon(mViewModel.mSelectCommunityData?.icon, mViewModel.mSelectCommunityData?.iconSubscript) - mForumContainerView.background = ContextCompat.getDrawable(this, R.drawable.bg_shape_f8_radius_4) - mGameName.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(this, R.drawable.ic_article_edit_choose_forum_arrow_gray), null) - mGameName.setTextColor(ContextCompat.getColor(this, R.color.text_333333)) + if (mViewModel.type == BbsType.GAME_BBS.value) { + mGameName.text = mViewModel.mSelectCommunityData?.name + mForumIcon.displayGameIcon(mViewModel.mSelectCommunityData?.icon, mViewModel.mSelectCommunityData?.iconSubscript) + setForumUI() + } else if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + if (mViewModel.gameEntity == null) { + mGameName.text = "选择游戏" + } else { + mGameName.text = mViewModel.gameEntity?.name + mForumIcon.displayGameIcon(mViewModel.gameEntity?.icon, mViewModel.gameEntity?.iconSubscript) + setForumUI() + } + } + } else { + if (mViewModel.type == BbsType.GAME_BBS.value) { + mGameName.text = "选择论坛" + } else if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + mGameName.text = "选择游戏" + } } val beginTransaction = supportFragmentManager.beginTransaction() @@ -516,6 +481,13 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { checkPostButtonEnable() } + private fun setForumUI() { + mForumIcon.visibility = View.VISIBLE + mForumContainerView.background = ContextCompat.getDrawable(this, R.drawable.bg_shape_f8_radius_4) + mGameName.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(this, R.drawable.ic_article_edit_choose_forum_arrow_gray), null) + mGameName.setTextColor(ContextCompat.getColor(this, R.color.text_333333)) + } + override fun onMenuItemClick(item: MenuItem?): Boolean { if (item?.itemId == R.id.menu_answer_post) { debounceActionWithInterval(R.id.menu_answer_post, 1000L) { @@ -562,33 +534,9 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { mEditTitle.filters = filterArray } - override fun onKeyboardHeightChanged(height: Int, orientation: Int) { - mIsKeyBoardShow = height > 0 - if (height > 0) { - changeAddLabel(false) - closeExtendedKeyboard() - } - } - - override fun onResume() { - super.onResume() - mKeyboardHeightProvider?.setKeyboardHeightObserver(this) - } - - override fun onPause() { - super.onPause() - mKeyboardHeightProvider?.setKeyboardHeightObserver(null) - } - - override fun onDestroy() { - super.onDestroy() - mKeyboardHeightProvider?.close() - } - companion object { const val SAVE_DRAFTS_INTERVAL_TIME = 15000 const val ARTICLE_DRAFT_REQUEST_CODE = 105 - const val REQUEST_CODE_IMAGE = 120 const val ARTICLE_DRAFT_CHANGE_TAG = "ANSWER_DRAFT_CHANGE_TAG" const val ARTICLE_POSTED_TAG = "ARTICLE_POSTED_TAG" @@ -598,8 +546,9 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { } @JvmStatic - fun getIntent(context: Context, community: CommunityEntity?): Intent { + fun getIntent(context: Context, community: CommunityEntity?, type: String = ""): Intent { val intent = Intent(context, ArticleEditActivity::class.java) + intent.putExtra(BbsType::class.java.simpleName, type) intent.putExtra(CommunityEntity::class.java.simpleName, community) return intent } diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditViewModel.kt index e1cc274607..7e237f3b43 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditViewModel.kt @@ -1,30 +1,27 @@ package com.gh.gamecenter.qa.article.edit import android.app.Application -import android.content.Intent import android.text.TextUtils import android.view.Gravity -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import com.gh.base.fragment.WaitingDialogFragment import com.gh.common.util.* import com.gh.gamecenter.R import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.entity.ErrorEntity import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity +import com.gh.base.BaseRichEditorViewModel +import com.gh.base.RichType +import com.gh.gamecenter.entity.ActivityLabelEntity +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.entity.ArticleDetailEntity import com.gh.gamecenter.qa.entity.ArticleDraftEntity import com.gh.gamecenter.qa.questions.edit.QuestionEditViewModel import com.gh.gamecenter.retrofit.Response -import com.gh.gamecenter.retrofit.RetrofitManager import com.lightgame.utils.Utils -import com.zhihu.matisse.Matisse -import com.zhihu.matisse.internal.utils.PathUtils import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import okhttp3.MediaType import okhttp3.RequestBody @@ -33,47 +30,26 @@ import org.greenrobot.eventbus.EventBus import org.json.JSONArray import org.json.JSONObject import retrofit2.HttpException -import java.io.File -import java.util.* import kotlin.collections.ArrayList -import kotlin.collections.HashMap -class ArticleEditViewModel(application: Application) : AndroidViewModel(application) { - - private val mApi = RetrofitManager.getInstance(getApplication()).api - - val MIN_ARTICLE_TEXT_LENGTH = 6 - val MAX_ARTICLE_TEXT_LENGTH = 10000 - - val processDialog = MediatorLiveData() +class ArticleEditViewModel(application: Application) : BaseRichEditorViewModel(application) { val postArticle = MediatorLiveData() val postArticleDrafts = MediatorLiveData>() val articleDraftsContent = MediatorLiveData() val error = MutableLiveData() val notSelectForum = MutableLiveData() - - val uploadingImage = ArrayList>() - val chooseImagesUpload = MutableLiveData>() - val chooseImagesUploadSuccess = MutableLiveData>() - - var uploadImageSubscription: Disposable? = null - var mSelectCommunityData: CommunityEntity? = null - - val mapImg = HashMap() - val titleTags = MediatorLiveData>() var defaultTags = MediatorLiveData>() val selectedTags: MutableList = ArrayList() var selectedTagsChange = MediatorLiveData() - var detailEntity: ArticleDetailEntity? = null var draftEntity: ArticleDraftEntity? = null - var title: String? = null var content: String? = null - var openArticleInNewPage: Boolean = false + var selectActivityLabelEntity: ActivityLabelEntity? = null + var gameEntity: GameEntity? = null /** * 获取默认标签和确定当前communityId @@ -102,17 +78,17 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat // 检查标题长度限制 title?.trim() title = title?.replace("\n", "") - if (title!!.length < QuestionEditViewModel.QUESTION_TITLE_MIN_LENGTH) { - ToastUtils.showToast("标题至少${QuestionEditViewModel.QUESTION_TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) + if (title!!.length < TITLE_MIN_LENGTH) { + ToastUtils.showToast("标题至少${TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) return false } val articleContent = HtmlUtils.stripHtml(content).length - if (articleContent < MIN_ARTICLE_TEXT_LENGTH) { - ToastUtils.showToast("正文至少${QuestionEditViewModel.QUESTION_TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) + if (articleContent < MIN_TEXT_LENGTH) { + ToastUtils.showToast("正文至少${TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) return false } - if (articleContent > MAX_ARTICLE_TEXT_LENGTH) { - ToastUtils.showToast("帖子最多输入${MAX_ARTICLE_TEXT_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) + if (articleContent > MAX_TEXT_LENGTH) { + ToastUtils.showToast("帖子最多输入${MAX_TEXT_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) return false } if (detailEntity != null && detailEntity?.title == title && detailEntity?.content == content) return false @@ -121,6 +97,10 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat notSelectForum.postValue(true) return false } + if (type == BbsType.OFFICIAL_BBS.value && gameEntity == null) { + ToastUtils.showToast("请选择游戏", if (isKeyBoardShow) Gravity.CENTER else -1) + return false + } return true } @@ -128,100 +108,15 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat if (mSelectCommunityData == null) return false if (TextUtils.isEmpty(title)) return false title = title?.replace("\n", "") - if (title!!.length < QuestionEditViewModel.QUESTION_TITLE_MIN_LENGTH) return false + if (title!!.length < TITLE_MIN_LENGTH) return false val articleContent = HtmlUtils.stripHtml(content).length - if (articleContent < MIN_ARTICLE_TEXT_LENGTH) return false - if (articleContent > MAX_ARTICLE_TEXT_LENGTH) return false + if (articleContent < MIN_TEXT_LENGTH) return false + if (articleContent > MAX_TEXT_LENGTH) return false if (detailEntity != null && detailEntity?.title == title && detailEntity?.content == content) return false + if (type == BbsType.OFFICIAL_BBS.value && gameEntity == null) return false return true } - /** - * 检查图片是否符合规则并上传图片 - * @param picPath 图片本地路径 - */ - fun uploadPic(data: Intent) { - val uris = Matisse.obtainResult(data) - val pictureList = ArrayList() - for (uri in uris) { - val picturePath = PathUtils.getPath(getApplication(), uri) - if (picturePath != null) { - if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) { - val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024 - val application: Application = getApplication() - Utils.toast(getApplication(), application.getString(R.string.pic_max_hint, count)) - continue - } - Utils.log("picturePath = $picturePath") - pictureList.add(picturePath) - } else { - Utils.log("picturePath is null") - } - } - - if (pictureList.size == 0) return - - uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(UploadImageUtils.UploadType.answer, pictureList - , false, object : UploadImageUtils.OnUploadImageListListener { - override fun onProgress(total: Long, progress: Long) { - } - - override fun onCompressSuccess(imageUrls: List) { - val chooseImageMd5Map = LinkedHashMap() - imageUrls.forEach { - chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = "" - } - uploadingImage.add(chooseImageMd5Map) - chooseImagesUpload.postValue(chooseImageMd5Map) - - } - - override fun onSuccess(imageUrl: LinkedHashMap, errorMap: Map) { - val uploadMap = uploadingImage.find { it.containsKey(MD5Utils.getUrlMD5(imageUrl.entries.iterator().next().key)) } - uploadMap?.let { - for (key in imageUrl.keys) { - uploadMap[MD5Utils.getUrlMD5(key)] = AnswerEditActivity.FILE_HOST + key.decodeURI() - mapImg[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: "" - } - chooseImagesUploadSuccess.postValue(uploadMap) - uploadingImage.remove(uploadMap) - } - val errorSize = pictureList.size - imageUrl.size - if (errorSize > 0) { - for (error in errorMap.values) { - if (error is HttpException && error.code() == 403) { - Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败") - return - } - } - Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败") - } - } - - override fun onError(errorMap: Map) { - val errorSize = pictureList.size - - for (error in errorMap.values) { - if (error is HttpException && error.code() == 403) { - val e = error.response()?.errorBody()?.string()?.toObject() - if (e != null && e.code == 403017) { - Utils.toast(getApplication(), errorSize.toString() + "张图片的宽或高超过限制,请裁剪后上传") - } else { - Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败") - } - - return - } - } - if (errorSize == 1) { - Utils.toast(getApplication(), "图片上传失败") - } else { - Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败") - } - } - }) - } - /** * 选择动作(选中标签/移除已选中的标签) */ @@ -346,17 +241,23 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat } private fun getArticleBody(): JSONObject { - val tags = JSONArray() - for (tag in selectedTags) { - tags.put(tag) - } val jsonObject = JSONObject() jsonObject.put("title", title) jsonObject.put("content", content) - jsonObject.put("tags", tags) if (detailEntity != null) { jsonObject.put("article_id", detailEntity?.id) } + if (selectActivityLabelEntity != null) { + jsonObject.put("tag_activity_id", selectActivityLabelEntity?.id) + jsonObject.put("tag_activity_name", selectActivityLabelEntity?.name) + } + if (type.isNotEmpty()) { + jsonObject.put("type", type) + } + if (gameEntity != null) { + jsonObject.put("game_id", gameEntity?.id) + } + jsonObject.put("tags", JSONArray()) return jsonObject } @@ -382,4 +283,6 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat } }) } + + override fun getRichType(): RichType = RichType.ARTICLE } diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleTagsSelectFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleTagsSelectFragment.kt index c59b157e11..dd66362a3b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleTagsSelectFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleTagsSelectFragment.kt @@ -76,7 +76,9 @@ class ArticleTagsSelectFragment : BaseFragment() { articleId, mEntrance, "撰写文章") startActivity(intent) } - toast("发布成功") + if (mViewModel?.checkIsAllUploadedAndToast() == true) { + toast("发布成功") + } activity?.finish() AppExecutor.uiExecutor.executeWithDelay(Runnable { NotificationHelper.showNotificationHintDialog(NotificationUgc.ARTICLE) @@ -90,7 +92,7 @@ class ArticleTagsSelectFragment : BaseFragment() { toast(R.string.questionsdetail_max_tag_hint) } } - mViewModel?.getDefaultTag() +// mViewModel?.getDefaultTag() } fun publishArticle() { @@ -148,7 +150,7 @@ class ArticleTagsSelectFragment : BaseFragment() { dialog.setOnDismissListener { this.view?.visibility = View.VISIBLE requireActivity().currentFocus?.clearFocus() - (requireActivity() as ArticleEditActivity).controlAddLabelContainer() +// (requireActivity() as ArticleEditActivity).controlAddLabelContainer() } dialog.setCancelable(false) dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/CommentActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/CommentActivity.kt index e86ae399c5..d7c6b34b0a 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/CommentActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/CommentActivity.kt @@ -14,7 +14,7 @@ import com.gh.gamecenter.NormalActivity import com.gh.gamecenter.R import com.gh.gamecenter.entity.CommentEntity import com.gh.gamecenter.qa.answer.detail.AnswerDetailFragment -import com.gh.gamecenter.qa.article.detail.comment.ArticleDetailCommentFragment +import com.gh.gamecenter.qa.comment.conversation.CommentConversationFragment import com.lightgame.utils.Util_System_Keyboard import kotlinx.android.synthetic.main.activity_comment.* @@ -28,22 +28,21 @@ class CommentActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) DisplayUtils.transparentStatusBar(this) - val answerId = intent.getStringExtra(ANSWER_ID) val commentCount = intent.getIntExtra(COMMENT_COUNT, 0) val showKeyboard = intent.getBooleanExtra(SHOW_KEYBOARD, false) + val isCommentConversation = intent.getBooleanExtra(EntranceUtils.KEY_IS_COMMENT_CONVERSATION, false) - val articleId = intent.getStringExtra(ARTICLE_ID) - + val answerId = intent.getStringExtra(ANSWER_ID) ?: "" + val articleId = intent.getStringExtra(ARTICLE_ID) ?: "" val communityId = intent.getStringExtra(COMMUNITY_ID) ?: "" - val videoId = intent.getStringExtra(VIDEO_ID) ?: "" + val questionId = intent.getStringExtra(QUESTION_ID) ?: "" + val commentId = intent.getStringExtra(EntranceUtils.KEY_COMMENT_ID) ?: "" + val isVideoAuthor = intent.getBooleanExtra(IS_VIDEO_AUTHOR, false) - - val commentEntity: CommentEntity? = intent.getParcelableExtra(COMMENT_ENTITY) ?: null - + val isStairsComment = intent.getBooleanExtra(IS_STAIRS_COMMENT, false) + val commentEntity: CommentEntity? = intent.getParcelableExtra(COMMENT_ENTITY) val useReplyApi: Boolean = intent.getBooleanExtra(USE_REPLY_API, false) - val articleCommentId = intent.getStringExtra(EntranceUtils.KEY_ARTICLE_COMMENT_ID) - mShowInputOnly = intent.getBooleanExtra(SHOW_INPUT_ONLY, false) val commentCallback = object : AnswerDetailFragment.CommentListener { @@ -63,40 +62,59 @@ class CommentActivity : BaseActivity() { commentFragment.setCommentListener(commentCallback) } } else { - commentFragment = if (!answerId.isNullOrEmpty()) { - NewCommentFragment.getAnswerCommentInstance( - answerId, - showKeyboard, - commentCount, - mShowInputOnly, - commentCallback) - } else if (!articleId.isNullOrEmpty()) { - NewCommentFragment.getCommunityArticleCommentInstance( - articleId, - communityId, - showKeyboard, - commentCount, - mShowInputOnly, - useReplyApi, - commentEntity, - commentCallback) - } else if (!articleCommentId.isNullOrEmpty()) { - ArticleDetailCommentFragment().with(intent.extras) - } else { - NewCommentFragment.getVideoCommentInstance( - videoId, - showKeyboard, - commentCount, - isVideoAuthor, - commentEntity, - commentCallback) + commentFragment = when { + isCommentConversation && commentId.isNotEmpty() -> { + CommentConversationFragment().with(intent.extras) + } + answerId.isNotEmpty() -> { + NewCommentFragment.getAnswerCommentInstance( + answerId, + showKeyboard, + commentCount, + mShowInputOnly, + commentCallback) + } + articleId.isNotEmpty() -> { + NewCommentFragment.getCommunityArticleCommentInstance( + articleId, + communityId, + showKeyboard, + commentCount, + mShowInputOnly, + useReplyApi, + commentEntity, + commentCallback) + } + questionId.isNotEmpty() -> { + NewCommentFragment.getCommunityQuestionCommentInstance( + questionId, + communityId, + showKeyboard, + commentCount, + mShowInputOnly, + useReplyApi, + commentEntity, + commentCallback) + } + else -> { + NewCommentFragment.getVideoCommentInstance( + videoId, + showKeyboard, + commentCount, + isVideoAuthor, + commentEntity, + mShowInputOnly, + isStairsComment, + useReplyApi, + commentCallback) + } } } supportFragmentManager.beginTransaction().replace(R.id.answerCommentPlaceholderView, commentFragment!!, NewCommentFragment::class.java.simpleName).commitNowAllowingStateLoss() maskView.alpha = 0f - if (videoId.isNullOrEmpty()) { + if (!isStairsComment) { maskView.animate().alpha(1f).setDuration(300).start() } answerCommentPlaceholderView.translationY = (DisplayUtils.getScreenHeight()).toFloat() @@ -128,8 +146,10 @@ class CommentActivity : BaseActivity() { const val COMMENT_ENTITY = "comment_entity" const val IS_VIDEO_AUTHOR = "isVideoAuthor" const val SHOW_KEYBOARD = "show_keyboard" + const val IS_STAIRS_COMMENT = "is_stairs_comment" const val ARTICLE_ID = "article_id" + const val QUESTION_ID = "question_id" const val COMMUNITY_ID = "community_id" const val SHOW_INPUT_ONLY = "show_input_only" @@ -158,39 +178,76 @@ class CommentActivity : BaseActivity() { return intent } + /** + * 评论视频 + */ @JvmStatic - fun getVideoCommentIntent(context: Context, videoId: String, commentCount: Int? = 0, isVideoAuthor: Boolean, showKeyboard: Boolean): Intent { + fun getVideoCommentIntent(context: Context, videoId: String, commentCount: Int? = 0, isVideoAuthor: Boolean, + showKeyboard: Boolean, showInputOnly: Boolean = false, isStairsComment: Boolean = true): Intent { val intent = Intent(context, CommentActivity::class.java) intent.putExtra(VIDEO_ID, videoId) intent.putExtra(COMMENT_COUNT, commentCount) intent.putExtra(IS_VIDEO_AUTHOR, isVideoAuthor) intent.putExtra(SHOW_KEYBOARD, showKeyboard) + intent.putExtra(SHOW_INPUT_ONLY, showInputOnly) + intent.putExtra(IS_STAIRS_COMMENT, isStairsComment) if (context is Activity) { context.overridePendingTransition(0, 0) } return intent } - @JvmStatic - fun getArticleDetailCommentIntent(context: Context, - commentId: String, - communityId: String, - articleId: String, - showKeyboard: Boolean = false, - position: Int = -1, - entrance: String, - path: String): Intent { + /** + * 回复视频评论 + */ + fun getVideoCommentReplyIntent(context: Context, videoId: String, commentCount: Int? = 0, isVideoAuthor: Boolean, + showKeyboard: Boolean, showInputOnly: Boolean = false, commentEntity: CommentEntity? = null): Intent { val intent = Intent(context, CommentActivity::class.java) - intent.putExtra(EntranceUtils.KEY_ENTRANCE, NormalActivity.mergeEntranceAndPath(entrance, path)) - intent.putExtra(EntranceUtils.KEY_COMMUNITY_ARTICLE_ID, articleId) - intent.putExtra(EntranceUtils.KEY_COMMUNITY_ID, communityId) - intent.putExtra(EntranceUtils.KEY_POSITION, position) - intent.putExtra(EntranceUtils.KEY_ARTICLE_COMMENT_ID, commentId) - intent.putExtra(EntranceUtils.KEY_SHOW_KEYBOARD_IF_NEEDED, showKeyboard) - intent.putExtra(EntranceUtils.KEY_PATH, path) + intent.putExtra(VIDEO_ID, videoId) + intent.putExtra(COMMENT_COUNT, commentCount) + intent.putExtra(IS_VIDEO_AUTHOR, isVideoAuthor) + intent.putExtra(SHOW_KEYBOARD, showKeyboard) + intent.putExtra(SHOW_INPUT_ONLY, showInputOnly) + intent.putExtra(IS_STAIRS_COMMENT, false) + intent.putExtra(COMMENT_ENTITY, commentEntity) + intent.putExtra(USE_REPLY_API, true) + if (context is Activity) { + context.overridePendingTransition(0, 0) + } return intent } + /** + * 回复帖子、视频、问题评论 + */ + @JvmStatic + fun getCommentDetailIntent(context: Context, + commentId: String, + communityId: String, + articleId: String, + videoId: String, + questionId: String, + showKeyboard: Boolean = false, + position: Int = -1, + entrance: String, + path: String): Intent { + val intent = Intent(context, CommentActivity::class.java) + intent.putExtra(EntranceUtils.KEY_ENTRANCE, NormalActivity.mergeEntranceAndPath(entrance, path)) + intent.putExtra(EntranceUtils.KEY_COMMUNITY_ARTICLE_ID, articleId) + intent.putExtra(VIDEO_ID, videoId) + intent.putExtra(QUESTION_ID, questionId) + intent.putExtra(EntranceUtils.KEY_COMMUNITY_ID, communityId) + intent.putExtra(EntranceUtils.KEY_POSITION, position) + intent.putExtra(EntranceUtils.KEY_COMMENT_ID, commentId) + intent.putExtra(EntranceUtils.KEY_SHOW_KEYBOARD_IF_NEEDED, showKeyboard) + intent.putExtra(EntranceUtils.KEY_PATH, path) + intent.putExtra(EntranceUtils.KEY_IS_COMMENT_CONVERSATION, true) + return intent + } + + /** + * 评论帖子 + */ @JvmStatic fun getArticleCommentIntent(context: Context, articleId: String, @@ -234,5 +291,51 @@ class CommentActivity : BaseActivity() { } return intent } + + /** + * 评论问题(回答) + */ + @JvmStatic + fun getQuestionCommentIntent(context: Context, + questionId: String, + commentCount: Int? = 0, + showKeyboard: Boolean, + communityId: String, + showInputOnly: Boolean = false): Intent { + val intent = Intent(context, CommentActivity::class.java) + intent.putExtra(QUESTION_ID, questionId) + intent.putExtra(COMMENT_COUNT, commentCount) + intent.putExtra(SHOW_KEYBOARD, showKeyboard) + intent.putExtra(COMMUNITY_ID, communityId) + intent.putExtra(SHOW_INPUT_ONLY, showInputOnly) + if (context is Activity) { + context.overridePendingTransition(0, 0) + } + return intent + } + + /** + * 回复问题评论 + */ + fun getQuestionCommentReplyIntent(context: Context, + questionId: String, + communityId: String, + commentCount: Int? = 0, + showKeyboard: Boolean, + showInputOnly: Boolean = false, + commentEntity: CommentEntity? = null): Intent { + val intent = Intent(context, CommentActivity::class.java) + intent.putExtra(QUESTION_ID, questionId) + intent.putExtra(COMMENT_COUNT, commentCount) + intent.putExtra(SHOW_KEYBOARD, showKeyboard) + intent.putExtra(COMMUNITY_ID, communityId) + intent.putExtra(SHOW_INPUT_ONLY, showInputOnly) + intent.putExtra(COMMENT_ENTITY, commentEntity) + intent.putExtra(USE_REPLY_API, true) + if (context is Activity) { + context.overridePendingTransition(0, 0) + } + return intent + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt index 869858268a..e0a6637d38 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt @@ -2,7 +2,6 @@ package com.gh.gamecenter.qa.comment import android.content.Context import android.text.SpannableStringBuilder -import android.text.TextUtils import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat @@ -16,7 +15,6 @@ import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.entity.CommentEntity import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity -import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentAdapter class NewCommentAdapter(context: Context, var mViewModel: NewCommentViewModel, @@ -118,7 +116,7 @@ class NewCommentAdapter(context: Context, holder.commentLikeContainer.setOnClickListener { CommentUtils.likeComment(mContext, mViewModel.answerId, mViewModel.articleId, - mViewModel.communityId, mViewModel.videoId, commentEntity, holder.commentLikeCountTv, holder.commentLikeIv, null) + mViewModel.communityId, mViewModel.videoId, mViewModel.questionId, commentEntity, holder.commentLikeCountTv, holder.commentLikeIv, null) } holder.itemView.setOnClickListener { @@ -177,6 +175,9 @@ class NewCommentAdapter(context: Context, CommentType.VIDEO, CommentType.VIDEO_CONVERSATION -> "视频详情-评论管理" + + CommentType.COMMUNITY_QUESTION, + CommentType.COMMUNITY_QUESTION_CONVERSATION -> "问题详情-评论管理" } val userHomePageTabPosition = if (mViewModel.commentType.isVideo()) 2 else 1 diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt index 682fe0d6b0..57c87b7a89 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt @@ -12,6 +12,7 @@ import android.widget.* import androidx.annotation.Nullable import androidx.core.content.ContextCompat import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView import butterknife.BindView @@ -33,6 +34,7 @@ import com.gh.gamecenter.eventbus.EBCommentSuccess import com.gh.gamecenter.eventbus.EBDeleteComment import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.qa.answer.detail.AnswerDetailFragment +import com.gh.gamecenter.qa.comment.CommentActivity.Companion.QUESTION_ID import com.halo.assistant.HaloApp import com.lightgame.utils.Util_System_Keyboard import org.greenrobot.eventbus.EventBus @@ -80,6 +82,7 @@ open class NewCommentFragment : ListFragment protected var mArticleId: String = "" protected var mCommunityId: String = "" protected var mVideoId: String = "" + protected var mQuestionId: String = "" protected var mCommentId: String = "" protected var mShowInputOnly: Boolean = false // 是否只显示输入框,不显示列表 protected var mIsVideoAuthor: Boolean = false//是否是视频作者 @@ -102,6 +105,7 @@ open class NewCommentFragment : ListFragment mAnswerId = getString(ANSWER_ID, "") mArticleId = getString(ARTICLE_ID, "") mVideoId = getString(VIDEO_ID, "") + mQuestionId = getString(CommentActivity.QUESTION_ID, "") mCommentCount = getInt(COMMENT_COUNT, 0) mCommentType = getSerializable(COMMENT_TYPE) as? CommentType ?: CommentType.ANSWER mCommunityId = getString(COMMUNITY_ID, "") @@ -306,6 +310,7 @@ open class NewCommentFragment : ListFragment communityId = mCommunityId, commentId = mCommentId, videoId = mVideoId, + questionId = mQuestionId, commentType = mCommentType, isVideoAuthor = mIsVideoAuthor)) return mViewModel @@ -320,6 +325,9 @@ open class NewCommentFragment : ListFragment CommentType.COMMUNITY_ARTICLE, CommentType.COMMUNITY_ARTICLE_CONVERSATION -> "(文章详情-评论列表)" + CommentType.COMMUNITY_QUESTION, + CommentType.COMMUNITY_QUESTION_CONVERSATION -> "(问题详情-评论列表)" + CommentType.VIDEO, CommentType.VIDEO_CONVERSATION -> "(视频详情-评论列表)" } @@ -455,6 +463,15 @@ open class NewCommentFragment : ListFragment "视频详情-评论-回复" } } + + CommentType.COMMUNITY_QUESTION, + CommentType.COMMUNITY_QUESTION_CONVERSATION -> { + if (mCommentEntity == null) { + "问题详情-评论-写评论" + } else { + "问题详情-评论-回复" + } + } } } @@ -465,32 +482,6 @@ open class NewCommentFragment : ListFragment } open fun popInputLayout(isPopup: Boolean, height: Int) { - /*mCommentLine.goneIf(isPopup) - mShadowView.goneIf(mShowInputOnly || !isPopup) - if (requireActivity() is CommentDetailActivity) { - val shadowView = (requireActivity() as CommentDetailActivity).shadowView - shadowView.visibility = if (isPopup && !mShowInputOnly) View.VISIBLE else View.GONE - DisplayUtils.setLightStatusBar(requireActivity(), !isPopup) - shadowView.setOnClickListener { Util_System_Keyboard.hideSoftKeyboard(activity) } - } - mAnswerContent.orientation = if (isPopup) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL - if (isPopup) { - mAnswerContent.background = ContextCompat.getDrawable(requireActivity(), R.drawable.bg_shape_white_radius_10_top_only) - } else { - mAnswerContent.setBackgroundColor(ContextCompat.getColor(requireActivity(), R.color.white)) - mOffset = Math.abs(height) - } - - val mScrollViewParams = mScrollView.layoutParams as LinearLayout.LayoutParams - mScrollViewParams.width = if (isPopup) LinearLayout.LayoutParams.MATCH_PARENT else 0 - mScrollViewParams.height = if (isPopup) DisplayUtils.dip2px(64f) else DisplayUtils.dip2px(28f) - mScrollView.layoutParams = mScrollViewParams - - val mLayoutParams = commentInputContainer.layoutParams as FrameLayout.LayoutParams - mLayoutParams.height = if (isPopup) DisplayUtils.dip2px(130f) else LinearLayout.LayoutParams.WRAP_CONTENT - mLayoutParams.bottomMargin = if (isPopup) height + mOffset - DisplayUtils.dip2px(12F) else 0 - commentInputContainer.layoutParams = mLayoutParams*/ - mCommentLine.goneIf(mShowInputOnly || isPopup) mShadowView.goneIf(mShowInputOnly || !isPopup) if (requireActivity() is CommentDetailActivity) { @@ -574,6 +565,7 @@ open class NewCommentFragment : ListFragment const val SHOW_INPUT_ONLY = "showInputOnly" const val COMMENT_ENTITY = "commentEntity" const val IS_VIDEO_AUTHOR = "isVideoAuthor" + const val IS_STAIRS_COMMENT = "is_stairs_comment" fun getAnswerCommentInstance(answerId: String, showSoftKeyboardOnStartUp: Boolean, @@ -621,24 +613,79 @@ open class NewCommentFragment : ListFragment } } + fun getCommunityQuestionCommentInstance(questionId: String, + communityId: String, + showSoftKeyboardOnStartUp: Boolean, + commentCount: Int, + showInputOnly: Boolean, + useReplyApi: Boolean, + commentEntity: CommentEntity?, + listener: AnswerDetailFragment.CommentListener) + : NewCommentFragment { + val commentType = if (useReplyApi) { + CommentType.COMMUNITY_QUESTION_CONVERSATION + } else { + CommentType.COMMUNITY_QUESTION + } + return NewCommentFragment().apply { + mCommentListener = listener + with(bundleOf( + SHOW_SOFT_KEY_BOARD_ON_STARTUP to showSoftKeyboardOnStartUp, + QUESTION_ID to questionId, + COMMENT_COUNT to commentCount, + COMMENT_TYPE to commentType, + COMMUNITY_ID to communityId, + SHOW_INPUT_ONLY to showInputOnly, + COMMENT_ENTITY to commentEntity + )) + } + } + fun getVideoCommentInstance(videoId: String, showSoftKeyboardOnStartUp: Boolean, commentCount: Int, isVideoAuthor: Boolean, commentEntity: CommentEntity? = null, + showInputOnly: Boolean, + isStairsComment: Boolean, + useReplyApi: Boolean, listener: AnswerDetailFragment.CommentListener) - : StairsCommentFragment { - return StairsCommentFragment().apply { - mCommentListener = listener - with(bundleOf( - SHOW_SOFT_KEY_BOARD_ON_STARTUP to showSoftKeyboardOnStartUp, - VIDEO_ID to videoId, - COMMENT_COUNT to commentCount, - COMMENT_TYPE to CommentType.VIDEO, - COMMENT_ENTITY to commentEntity, - IS_VIDEO_AUTHOR to isVideoAuthor - )) + : Fragment { + return if (isStairsComment) { + StairsCommentFragment().apply { + mCommentListener = listener + with(bundleOf( + SHOW_SOFT_KEY_BOARD_ON_STARTUP to showSoftKeyboardOnStartUp, + VIDEO_ID to videoId, + COMMENT_COUNT to commentCount, + COMMENT_TYPE to CommentType.VIDEO, + COMMENT_ENTITY to commentEntity, + IS_VIDEO_AUTHOR to isVideoAuthor + )) + } + } else { + val commentType = if (useReplyApi) { + CommentType.VIDEO_CONVERSATION + } else { + CommentType.VIDEO + } + NewCommentFragment().apply { + mCommentListener = listener + with(bundleOf( + SHOW_SOFT_KEY_BOARD_ON_STARTUP to showSoftKeyboardOnStartUp, + VIDEO_ID to videoId, + COMMENT_COUNT to commentCount, + COMMENT_TYPE to CommentType.VIDEO, + COMMENT_ENTITY to commentEntity, + SHOW_INPUT_ONLY to showInputOnly, + IS_STAIRS_COMMENT to isStairsComment, + IS_VIDEO_AUTHOR to isVideoAuthor, + COMMENT_TYPE to commentType + )) + } } + + } } } diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt index fdf8965f3b..0d80a3e9ab 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt @@ -31,6 +31,7 @@ open class NewCommentViewModel(application: Application, var articleId: String = "", var communityId: String = "", var videoId: String = "", + var questionId: String = "", var commentType: CommentType = CommentType.ANSWER, var isVideoAuthor: Boolean = false) : ListViewModel(application) { @@ -63,8 +64,9 @@ open class NewCommentViewModel(application: Application, CommentType.COMMUNITY_ARTICLE -> api.getCommunityArticleCommentList(communityId, articleId, "time.create:1", page) CommentType.COMMUNITY_ARTICLE_CONVERSATION -> api.getCommunityArticleCommentConversation(communityId, articleId, commentId, page) - CommentType.VIDEO -> api.getVideoCommentList(videoId, page) + CommentType.VIDEO -> api.getVideoCommentList(videoId, page, mapOf()) CommentType.VIDEO_CONVERSATION -> api.getVideoCommentConversationList(videoId, commentId, page) + else -> null } } @@ -93,6 +95,14 @@ open class NewCommentViewModel(application: Application, } } + CommentType.COMMUNITY_QUESTION, CommentType.COMMUNITY_QUESTION_CONVERSATION -> { + if (commentEntity == null) { + api.postQuestionComment(questionId, body) + } else { + api.postReplyToQuestionComment(questionId, commentEntity.id, body) + } + } + CommentType.VIDEO, CommentType.VIDEO_CONVERSATION -> { if (commentEntity == null) { api.postNewCommentToVideo(videoId, body) @@ -243,6 +253,7 @@ open class NewCommentViewModel(application: Application, private val articleId: String = "", private val communityId: String = "", private val videoId: String = "", + private val questionId: String = "", private val isVideoAuthor: Boolean = false, private val commentType: CommentType) : ViewModelProvider.NewInstanceFactory() { @@ -253,6 +264,7 @@ open class NewCommentViewModel(application: Application, articleId = articleId, communityId = communityId, videoId = videoId, + questionId = questionId, commentId = commentId, commentType = commentType, isVideoAuthor = isVideoAuthor) as T @@ -267,6 +279,9 @@ enum class CommentType { COMMUNITY_ARTICLE, COMMUNITY_ARTICLE_CONVERSATION, + COMMUNITY_QUESTION, + COMMUNITY_QUESTION_CONVERSATION, + VIDEO, VIDEO_CONVERSATION; diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt index 3c85671e5d..12be0a6886 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt @@ -38,6 +38,9 @@ class StairsCommentFragment : NewCommentFragment() { CommentType.VIDEO, CommentType.VIDEO_CONVERSATION -> "(视频详情-评论列表)" + + CommentType.COMMUNITY_QUESTION, + CommentType.COMMUNITY_QUESTION_CONVERSATION -> "(问题详情-评论列表)" } mAdapter = StairsCommentAdapter(requireContext(), mViewModel, true, this, this, entrance) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt index c6c8652444..ea6c9dce03 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt @@ -197,6 +197,9 @@ class StairsCommentViewHolder(val binding: StairsCommentItemBinding, val isReply CommentType.VIDEO, CommentType.VIDEO_CONVERSATION -> "视频详情-评论管理" + + CommentType.COMMUNITY_QUESTION, + CommentType.COMMUNITY_QUESTION_CONVERSATION -> "问题详情-评论管理" } holder.binding.commentUserIcon.setOnClickListener { DirectUtils.directToHomeActivity(binding.root.context, commentEntity.user.id, 2, mEntrance, path) } holder.binding.commentUserName.setOnClickListener { DirectUtils.directToHomeActivity(binding.root.context, commentEntity.user.id, 2, mEntrance, path) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/BaseArticleDetailCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt similarity index 60% rename from app/src/main/java/com/gh/gamecenter/qa/article/detail/BaseArticleDetailCommentAdapter.kt rename to app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt index 228faed1e8..d35ac9c83c 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/BaseArticleDetailCommentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt @@ -1,4 +1,4 @@ -package com.gh.gamecenter.qa.article.detail +package com.gh.gamecenter.qa.comment.base import android.annotation.SuppressLint import android.content.Context @@ -6,6 +6,7 @@ import android.text.SpannableStringBuilder import android.view.Gravity import android.view.View import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.ItemViewType @@ -22,18 +23,22 @@ import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding import com.gh.gamecenter.databinding.ItemArticleDetailCommentFooterBinding import com.gh.gamecenter.databinding.PieceArticleDetailCommentFilterBinding import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.qa.article.detail.ArticleDetailAdapter +import com.gh.gamecenter.qa.article.detail.ArticleDetailViewModel +import com.gh.gamecenter.qa.article.detail.CommentItemData import com.gh.gamecenter.qa.comment.CommentActivity import com.gh.gamecenter.qa.comment.OnCommentOptionClickListener import com.gh.gamecenter.qa.entity.ArticleDetailEntity +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity -abstract class BaseArticleDetailCommentAdapter(context: Context, - private var mViewModel: BaseArticleDetailCommentViewModel, - private var mType: AdapterType, - private var mEntrance: String, - var commentClosure: ((CommentEntity) -> Unit)? = null) +abstract class BaseCommentAdapter(context: Context, + private var mViewModel: BaseCommentViewModel, + private var mType: AdapterType, + private var mEntrance: String, + var commentClosure: ((CommentEntity) -> Unit)? = null) : ListAdapter(context), ISyncAdapterHandler { - var filterVH: ArticleDetailCommentFilterViewHolder? = null + var filterVH: CommentFilterViewHolder? = null override fun loadChange(status: LoadStatus?) { if (status == LoadStatus.INIT) { @@ -66,25 +71,25 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, return when (viewType) { ItemViewType.ITEM_FOOTER -> { val binding: ItemArticleDetailCommentFooterBinding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_article_detail_comment_footer, parent, false) - ArticleDetailCommentFooterViewHolder(binding) + CommentFooterViewHolder(binding) } ITEM_COMMENT_NORMAL -> { val binding: ItemArticleDetailCommentBinding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_article_detail_comment, parent, false) - ArticleDetailCommentViewHolder(binding, mType) + CommentItemViewHolder(binding, mType) } ITEM_FILTER -> { val binding: PieceArticleDetailCommentFilterBinding = DataBindingUtil.inflate(mLayoutInflater, R.layout.piece_article_detail_comment_filter, parent, false) - ArticleDetailCommentFilterViewHolder(binding).apply { filterVH = this } + CommentFilterViewHolder(binding).apply { filterVH = this } } ITEM_ERROR_EMPTY -> { - ArticleDetailCommentErrorViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false)) + CommentErrorViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false)) } ITEM_ERROR_CONNECTION -> { - ArticleDetailCommentErrorViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false)) + CommentErrorViewHolder(mLayoutInflater.inflate(R.layout.item_article_detail_comment_empty, parent, false)) } else -> throw IllegalAccessException() @@ -95,6 +100,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, val item = mEntityList[position] return when { item.articleDetail != null -> ITEM_ARTICLE_DETAIL + item.questionDetail != null -> ITEM_QUESTION_DETAIL item.filter != null -> ITEM_FILTER item.commentTop != null -> ITEM_COMMENT_TOP item.commentNormal != null -> ITEM_COMMENT_NORMAL @@ -110,7 +116,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { - is ArticleDetailCommentViewHolder -> { + is CommentItemViewHolder -> { holder.bindComment(mEntityList[position].commentNormal!!, mViewModel, mEntrance, commentClosure) { deleteCommentEntity -> val findEntity = mEntityList.find { it.commentNormal != null && it.commentNormal?.id == deleteCommentEntity.id } val index = mEntityList.indexOf(findEntity) @@ -142,7 +148,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, } } - is ArticleDetailCommentFooterViewHolder -> { + is CommentFooterViewHolder -> { holder.bindView(mIsLoading, mIsNetworkError, mIsOver) } } @@ -155,7 +161,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, return Pair(comment.id ?: "", comment) } - inner class ArticleDetailCommentFooterViewHolder(var binding: ItemArticleDetailCommentFooterBinding) : RecyclerView.ViewHolder(binding.root) { + inner class CommentFooterViewHolder(var binding: ItemArticleDetailCommentFooterBinding) : RecyclerView.ViewHolder(binding.root) { fun bindView(isLoading: Boolean, isNetworkError: Boolean, isOver: Boolean) { when { isNetworkError -> { @@ -178,17 +184,29 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, } } - inner class ArticleDetailCommentErrorViewHolder(view: View) : RecyclerView.ViewHolder(view) - inner class ArticleDetailCommentFilterViewHolder(var binding: PieceArticleDetailCommentFilterBinding) : RecyclerView.ViewHolder(binding.root) { - fun bindView(article: ArticleDetailEntity? = null, comment: CommentEntity? = null) { + inner class CommentErrorViewHolder(view: View) : RecyclerView.ViewHolder(view) + inner class CommentFilterViewHolder(var binding: PieceArticleDetailCommentFilterBinding) : RecyclerView.ViewHolder(binding.root) { + fun bindView(article: ArticleDetailEntity? = null, questions: QuestionsDetailEntity? = null, comment: CommentEntity? = null) { binding.run { val commentCount = mViewModel.commentCount - - commentHintTv.text = if (article != null) "全部评论" else "全部回复" + commentHintTv.text = when { + article != null -> { + "全部评论" + } + questions != null -> { + "全部回答" + } + comment != null -> { + "全部回复" + } + else -> { + "" + } + } commentHintCountTv.text = NumberUtils.transSimpleCount(commentCount) filterLatestTv.setOnClickListener { - mViewModel.changeSort(BaseArticleDetailCommentViewModel.SortType.LATEST) + mViewModel.changeSort(BaseCommentViewModel.SortType.LATEST) updateSortType() if (article == null) { @@ -198,7 +216,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, } } filterOldestTv.setOnClickListener { - mViewModel.changeSort(BaseArticleDetailCommentViewModel.SortType.OLDEST) + mViewModel.changeSort(BaseCommentViewModel.SortType.OLDEST) updateSortType() if (article == null) { @@ -212,7 +230,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, private fun updateSortType() { binding.run { - val isLatestSelected = mViewModel.currentSortType == BaseArticleDetailCommentViewModel.SortType.LATEST + val isLatestSelected = mViewModel.currentSortType == BaseCommentViewModel.SortType.LATEST val selectedTextView = if (isLatestSelected) filterLatestTv else filterOldestTv val unselectedTextView = if (!isLatestSelected) filterLatestTv else filterOldestTv @@ -222,7 +240,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, } } - class ArticleDetailCommentViewHolder(var binding: ItemArticleDetailCommentBinding, var type: AdapterType) + class CommentItemViewHolder(var binding: ItemArticleDetailCommentBinding, var type: AdapterType) : RecyclerView.ViewHolder(binding.root) { init { @@ -235,7 +253,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, @SuppressLint("SetTextI18n") fun bindComment(comment: CommentEntity, - viewModel: BaseArticleDetailCommentViewModel, + viewModel: BaseCommentViewModel, entrance: String, commentClosure: ((CommentEntity) -> Unit)? = null, deleteCallBack: ((comment: CommentEntity) -> Unit)? = null) { bindComment(binding, viewModel, comment, entrance, deleteCallBack) @@ -244,13 +262,27 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, updateSubComment(comment, viewModel, entrance) binding.floorHintTv.text = if (comment.floor != 0) "${comment.floor}楼" else "" binding.root.setOnClickListener { - CommentActivity.getArticleCommentIntent(binding.root.context, viewModel.articleId, viewModel.commentCount, true, viewModel.communityId, comment, true).apply { - binding.root.context.startActivity(this) + when { + viewModel.articleId.isNotEmpty() -> { + CommentActivity.getArticleCommentIntent(binding.root.context, viewModel.articleId, viewModel.commentCount, true, viewModel.communityId, comment, true).apply { + (binding.root.context as AppCompatActivity).startActivityForResult(this, CommentActivity.REQUEST_CODE) + } + MtaHelper.onEvent("帖子详情", "全部评论", "评论正文") + } + viewModel.videoId.isNotEmpty() -> { + CommentActivity.getVideoCommentReplyIntent(binding.root.context, viewModel.videoId, viewModel.commentCount, comment.me?.isContentOwner == true, true, true, comment).apply { + (binding.root.context as AppCompatActivity).startActivityForResult(this, CommentActivity.REQUEST_CODE) + } + } + viewModel.questionId.isNotEmpty() -> { + CommentActivity.getQuestionCommentReplyIntent(binding.root.context, viewModel.questionId, viewModel.communityId, viewModel.commentCount, true, true, comment).apply { + (binding.root.context as AppCompatActivity).startActivityForResult(this, CommentActivity.REQUEST_CODE) + } + } } - MtaHelper.onEvent("帖子详情", "全部评论", "评论正文") } binding.commentCountTv.setOnClickListener { - CommentActivity.getArticleDetailCommentIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, false, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply { + CommentActivity.getCommentDetailIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, viewModel.videoId, viewModel.questionId, false, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply { binding.root.context.startActivity(this) } MtaHelper.onEvent("帖子详情", "全部评论", "回复") @@ -299,9 +331,19 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, } @SuppressLint("SetTextI18n") - fun updateSubComment(comment: CommentEntity, viewModel: BaseArticleDetailCommentViewModel, entrance: String) { + fun updateSubComment(comment: CommentEntity, viewModel: BaseCommentViewModel, entrance: String) { val subCommentList = comment.subCommentList - val articleOwnerUserId = viewModel.topItemData?.articleDetail?.user?.id ?: "" + val ownerUserId = when { + viewModel.articleId.isNotEmpty() -> { + viewModel.topItemData?.articleDetail?.user?.id ?: "" + } + viewModel.questionId.isNotEmpty() -> { + viewModel.topItemData?.questionDetail?.user?.id ?: "" + } + else -> { + "" + } + } binding.moreSubCommentBtn.goneIf(comment.reply < 3) binding.moreSubCommentBtn.text = "查看全部${comment.reply}条回复" binding.subCommentContainer.goneIf(subCommentList.isNullOrEmpty()) @@ -310,13 +352,13 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, binding.subCommentContainer.setRoundedColorBackground(R.color.text_F5F5F5, 5F) subCommentList?.firstOrNull()?.let { - binding.firstSubCommentTv.text = getSubCommentSpanned(it.user.name, if (it.user.id == articleOwnerUserId) "作者" else "", it.content) + binding.firstSubCommentTv.text = getSubCommentSpanned(it.user.name, if (it.user.id == ownerUserId) "作者" else "", it.content) } subCommentList?.secondOrNull()?.let { - binding.secondSubCommentTv.text = getSubCommentSpanned(it.user.name, if (it.user.id == articleOwnerUserId) "作者" else "", it.content) + binding.secondSubCommentTv.text = getSubCommentSpanned(it.user.name, if (it.user.id == ownerUserId) "作者" else "", it.content) } binding.subCommentContainer.setOnClickListener { - CommentActivity.getArticleDetailCommentIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, false, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply { + CommentActivity.getCommentDetailIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, viewModel.videoId, viewModel.questionId, false, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply { binding.root.context.startActivity(this) } } @@ -335,7 +377,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, companion object { fun bindComment(binding: ItemArticleDetailCommentBinding, - viewModel: BaseArticleDetailCommentViewModel, + viewModel: BaseCommentViewModel, comment: CommentEntity, entrance: String, deleteCallBack: ((comment: CommentEntity) -> Unit)? = null) { val path = if (viewModel is ArticleDetailViewModel) PATH_ARTICLE_DETAIL else PATH_ARTICLE_DETAIL_COMMENT @@ -386,55 +428,116 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, binding.badgeIv.setOnClickListener { binding.badgeTv.performClick() } binding.moreIv.setOnClickListener { - CommentHelper.showCommunityArticleCommentOptions( - it, - comment, - false, - viewModel.articleId, - viewModel.communityId, - isShowTop = path == PATH_ARTICLE_DETAIL, - ignoreModerator = true, - listener = object : OnCommentOptionClickListener { - override fun onCommentOptionClick(entity: CommentEntity, option: String) { - when (option) { - "删除评论" -> { - DialogUtils.showNewAlertDialog(binding.moreIv.context, "提示", "删除评论后,评论下所有的回复都将被删除", "取消", "删除", null, { - viewModel.hideCommunityArticleComment(comment.id - ?: "") { - deleteCallBack?.invoke(comment) - } - }) - } - "置顶" -> { - DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, { - commentTop(binding.root.context, viewModel, comment, false) - }) - } - "取消置顶" -> { - DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论取消置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, { - viewModel.updateCommentTop(comment.id - ?: "", top = false, isAgain = false) { isSuccess, errorCode -> - if (isSuccess) { - viewModel.load(LoadType.REFRESH) - } - } - }) - } - } - - } - - }) - - if (viewModel is ArticleDetailViewModel) { - MtaHelper.onEvent("帖子详情", "全部评论", "更多") - } else { - MtaHelper.onEvent("帖子详情", "评论详情", "更多") + when { + viewModel.articleId.isNotEmpty() -> { + showCommunityArticleCommentOptions(it, binding, comment, viewModel, path, deleteCallBack) + } + viewModel.videoId.isNotEmpty() -> { + showVideoCommentOptions(it, binding, comment, viewModel, deleteCallBack) + } + viewModel.questionId.isNotEmpty() -> { + showQuestionCommentOption(it, binding, comment, viewModel, deleteCallBack) + } } } } - private fun commentTop(context: Context, viewModel: BaseArticleDetailCommentViewModel, comment: CommentEntity, isAgain: Boolean) { + private fun showQuestionCommentOption(view: View, binding: ItemArticleDetailCommentBinding, comment: CommentEntity, viewModel: BaseCommentViewModel, deleteCallBack: ((comment: CommentEntity) -> Unit)?) { + CommentHelper.showQuestionCommentOption(view, comment, viewModel.questionId, object : OnCommentOptionClickListener { + override fun onCommentOptionClick(entity: CommentEntity, option: String) { + when (option) { + "删除评论" -> { + DialogUtils.showNewAlertDialog(binding.moreIv.context, "提示", "删除评论后,评论下所有的回复都将被删除", "取消", "删除", null, { + viewModel.deleteComment(comment) { + deleteCallBack?.invoke(comment) + } + }) + } + "置顶" -> { + DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, { + commentTop(binding.root.context, viewModel, comment, false) + }) + } + "取消置顶" -> { + DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论取消置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, { + viewModel.updateCommentTop(comment.id + ?: "", top = false, isAgain = false) { isSuccess, errorCode -> + if (isSuccess) { + viewModel.load(LoadType.REFRESH) + } + } + }) + } + } + } + }) + } + + private fun showVideoCommentOptions(view: View, binding: ItemArticleDetailCommentBinding, comment: CommentEntity, viewModel: BaseCommentViewModel, deleteCallBack: ((comment: CommentEntity) -> Unit)?) { + CommentHelper.showVideoCommentOptions(view, comment, false, viewModel.videoId, + comment.me?.isContentOwner == true, object : OnCommentOptionClickListener { + override fun onCommentOptionClick(entity: CommentEntity, option: String) { + when (option) { + "删除评论" -> { + DialogUtils.showNewAlertDialog(binding.moreIv.context, "提示", "删除评论后,评论下所有的回复都将被删除", "取消", "删除", null, { + viewModel.deleteComment(comment) { + deleteCallBack?.invoke(comment) + } + }) + } + } + } + }) + } + + private fun showCommunityArticleCommentOptions(view: View, binding: ItemArticleDetailCommentBinding, comment: CommentEntity, viewModel: BaseCommentViewModel, path: String, deleteCallBack: ((comment: CommentEntity) -> Unit)?) { + CommentHelper.showCommunityArticleCommentOptions( + view, + comment, + false, + viewModel.articleId, + viewModel.communityId, + isShowTop = path == PATH_ARTICLE_DETAIL, + ignoreModerator = true, + listener = object : OnCommentOptionClickListener { + override fun onCommentOptionClick(entity: CommentEntity, option: String) { + when (option) { + "删除评论" -> { + DialogUtils.showNewAlertDialog(binding.moreIv.context, "提示", "删除评论后,评论下所有的回复都将被删除", "取消", "删除", null, { + viewModel.deleteComment(comment) { + deleteCallBack?.invoke(comment) + } + }) + } + "置顶" -> { + DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, { + commentTop(binding.root.context, viewModel, comment, false) + }) + } + "取消置顶" -> { + DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论取消置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, { + viewModel.updateCommentTop(comment.id + ?: "", top = false, isAgain = false) { isSuccess, errorCode -> + if (isSuccess) { + viewModel.load(LoadType.REFRESH) + } + } + }) + } + } + + } + + }) + + if (viewModel is ArticleDetailViewModel) { + MtaHelper.onEvent("帖子详情", "全部评论", "更多") + } else { + MtaHelper.onEvent("帖子详情", "评论详情", "更多") + } + } + + private fun commentTop(context: Context, viewModel: BaseCommentViewModel, comment: CommentEntity, isAgain: Boolean) { viewModel.updateCommentTop(comment.id ?: "", top = true, isAgain = isAgain) { isSuccess, errorCode -> if (isSuccess) { @@ -453,11 +556,12 @@ abstract class BaseArticleDetailCommentAdapter(context: Context, companion object { const val ITEM_ARTICLE_DETAIL = 801 - const val ITEM_FILTER = 802 - const val ITEM_COMMENT_TOP = 803 - const val ITEM_COMMENT_NORMAL = 804 - const val ITEM_ERROR_EMPTY = 805 - const val ITEM_ERROR_CONNECTION = 806 + const val ITEM_QUESTION_DETAIL = 802 + const val ITEM_FILTER = 803 + const val ITEM_COMMENT_TOP = 804 + const val ITEM_COMMENT_NORMAL = 805 + const val ITEM_ERROR_EMPTY = 806 + const val ITEM_ERROR_CONNECTION = 807 const val PATH_ARTICLE_DETAIL = "帖子详情" const val PATH_ARTICLE_DETAIL_COMMENT = "帖子评论详情" diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/BaseArticleDetailCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentFragment.kt similarity index 85% rename from app/src/main/java/com/gh/gamecenter/qa/article/detail/BaseArticleDetailCommentFragment.kt rename to app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentFragment.kt index 697a152753..865fafdd9c 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/BaseArticleDetailCommentFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentFragment.kt @@ -1,10 +1,9 @@ -package com.gh.gamecenter.qa.article.detail +package com.gh.gamecenter.qa.comment.base import android.graphics.drawable.InsetDrawable import android.os.Bundle import android.view.View import android.widget.TextView -import androidx.annotation.Nullable import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import butterknife.BindView @@ -13,11 +12,9 @@ import com.gh.common.view.CustomDividerItemDecoration import com.gh.common.view.vertical_recycler.SnappingLinearLayoutManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment -import com.gh.gamecenter.qa.article.detail.comment.ArticleDetailCommentFragment -import com.gh.gamecenter.qa.comment.CommentActivity -import kotterknife.bindView +import com.gh.gamecenter.qa.comment.conversation.CommentConversationFragment -abstract class BaseArticleDetailCommentFragment : ListFragment() { +abstract class BaseCommentFragment : ListFragment() { @BindView(R.id.fixedTopFilterView) lateinit var fixedTopFilterView: View @@ -43,7 +40,7 @@ abstract class BaseArticleDetailCommentFragment(application) { - + protected val mApi: ApiService = RetrofitManager.getInstance(application).api var currentSortType: SortType = SortType.OLDEST val loadResultLiveData = MutableLiveData() @@ -130,7 +130,7 @@ abstract class BaseArticleDetailCommentViewModel(application: Application, var a } fun like(comment: CommentEntity) { - PostCommentUtils.likeComment(getApplication(), null, articleId, communityId, null, comment.id, + PostCommentUtils.likeComment(getApplication(), null, articleId, communityId, videoId, questionId, comment.id, object : PostCommentUtils.PostCommentListener { override fun postSuccess(response: JSONObject?) { updateLike(comment.id ?: "", true) @@ -193,14 +193,24 @@ abstract class BaseArticleDetailCommentViewModel(application: Application, var a } } - fun hideCommunityArticleComment(commentId: String, callback: () -> Unit) { - RetrofitManager.getInstance(getApplication()).api - .hideCommunityArticleComment(communityId, articleId, commentId) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeOn(Schedulers.io()) + @SuppressLint("CheckResult") + fun deleteComment(entity: CommentEntity, callback: () -> Unit) { + val observable = when { + videoId.isNotEmpty() -> { + mApi.deleteVideoComment(videoId, entity.id).toObservable() + } + questionId.isNotEmpty() -> { + mApi.deleteQuestionComment(questionId, entity.id).toObservable() + } + articleId.isNotEmpty() -> { + mApi.hideCommunityArticleComment(communityId, articleId, entity.id) + } + else -> null + } ?: return + observable.compose(observableToMain()) .subscribe(object : Response() { override fun onResponse(response: ResponseBody?) { - hideCommunityArticleCommentSuccess() + hideCommentSuccess() callback.invoke() } @@ -208,7 +218,7 @@ abstract class BaseArticleDetailCommentViewModel(application: Application, var a super.onFailure(e) e?.let { httpException -> if (httpException.code() == 403) { - val string = e.response().errorBody()?.string() + val string = e.response().errorBody()?.string() ?: "" val errorJson = JSONObject(string) val errorCode = errorJson.getInt("code") if (errorCode == 403059) { @@ -229,10 +239,27 @@ abstract class BaseArticleDetailCommentViewModel(application: Application, var a if (isAgain) { map["again"] = isAgain } - RetrofitManager.getInstance(getApplication()).api.commentTop(communityId, articleId, commentId, map) + when { + articleId.isNotEmpty() -> { + mApi.postArticleCommentTop(communityId, articleId, commentId, map) + } + questionId.isNotEmpty() -> { + mApi.postQuestionCommentTop(questionId, commentId, map) + } + else -> null + } } else { - RetrofitManager.getInstance(getApplication()).api.commentUnTop(communityId, articleId, commentId) - } + when { + articleId.isNotEmpty() -> { + mApi.postArticleCommentUnTop(communityId, articleId, commentId) + } + questionId.isNotEmpty() -> { + mApi.postQuestionCommentUnTop(questionId, commentId) + } + else -> null + } + } ?: return + observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -256,8 +283,7 @@ abstract class BaseArticleDetailCommentViewModel(application: Application, var a } - - open fun hideCommunityArticleCommentSuccess() {} + open fun hideCommentSuccess() {} enum class LoadResult { SUCCESS, diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationAdapter.kt new file mode 100644 index 0000000000..b507b39a6b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationAdapter.kt @@ -0,0 +1,74 @@ +package com.gh.gamecenter.qa.comment.conversation + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.dip2px +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding +import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter + +class CommentConversationAdapter(context: Context, + var mViewModelCommunity: CommentConversationViewModel, + type: AdapterType, + private var mEntrance: String, + commentClosure: ((CommentEntity) -> Unit)? = null) + : BaseCommentAdapter(context, mViewModelCommunity, type, mEntrance, commentClosure) { + + var topCommentVH: TopCommentItemViewHolder? = null + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ITEM_COMMENT_TOP -> { + val binding: ItemArticleDetailCommentBinding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_article_detail_comment, parent, false) + TopCommentItemViewHolder(binding).apply { topCommentVH = this } + } + + else -> super.onCreateViewHolder(parent, viewType) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is TopCommentItemViewHolder -> { + holder.bindView(mEntityList[position].commentTop!!) + } + + else -> super.onBindViewHolder(holder, position) + } + } + + inner class TopCommentItemViewHolder(var binding: ItemArticleDetailCommentBinding) + : RecyclerView.ViewHolder(binding.root) { + + fun bindView(comment: CommentEntity) { + val constraintSet = ConstraintSet() + constraintSet.clone(binding.contentTv.parent as ConstraintLayout) + + constraintSet.clear(binding.contentTv.id, ConstraintSet.START) + constraintSet.connect(binding.contentTv.id, ConstraintSet.START, binding.userIconIv.id, ConstraintSet.START) + constraintSet.applyTo(binding.contentTv.parent as ConstraintLayout) + + val lp: ConstraintLayout.LayoutParams = binding.contentTv.layoutParams as ConstraintLayout.LayoutParams + lp.topMargin = 16F.dip2px() + lp.leftMargin = 8F.dip2px() + binding.contentTv.layoutParams = lp + + binding.comment = comment + binding.moreIv.visibility = View.GONE + binding.divider.visibility = View.VISIBLE + binding.commentCountTv.visibility = View.GONE + binding.floorHintTv.text = if (comment.floor != 0) "${comment.floor}楼" else "" + binding.contentTv.text = comment.content + binding.contentTv.maxLines = Int.MAX_VALUE + + CommentItemViewHolder.bindComment(binding, mViewModelCommunity, comment, mEntrance, null) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationFragment.kt new file mode 100644 index 0000000000..34b685369f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationFragment.kt @@ -0,0 +1,189 @@ +package com.gh.gamecenter.qa.comment.conversation + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import com.ethanhua.skeleton.Skeleton +import com.gh.common.AppExecutor +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.databinding.FragmentArticleDetailCommentBinding +import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter +import com.gh.gamecenter.qa.comment.base.BaseCommentFragment +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel +import com.gh.gamecenter.qa.article.detail.CommentItemData +import com.gh.gamecenter.qa.comment.CommentActivity +import com.halo.assistant.HaloApp + +class CommentConversationFragment : BaseCommentFragment() { + private lateinit var mViewModel: CommentConversationViewModel + private lateinit var mBinding: FragmentArticleDetailCommentBinding + private var mAdapterCommunity: CommentConversationAdapter? = null + + override fun getLayoutId() = R.layout.fragment_article_detail_comment + + override fun onCreate(savedInstanceState: Bundle?) { + mViewModel = provideListViewModel() + super.onCreate(savedInstanceState) + mViewModel.getComment() + mViewModel.positionInOriginList = arguments?.getInt(EntranceUtils.KEY_POSITION) + ?: -1 + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initView() + initObserver() + } + + override fun getInflatedLayout(): View { + return FragmentArticleDetailCommentBinding.inflate(LayoutInflater.from(requireContext()), null, false).apply { mBinding = this }.root + } + + override fun provideListAdapter(): ListAdapter<*> { + return mAdapterCommunity + ?: CommentConversationAdapter(requireContext(), mViewModel, BaseCommentAdapter.AdapterType.SUB_COMMENT, mEntrance) { + if (it.user.id == UserManager.getInstance().userId) { + toast("不能回复自己") + } else { + startCommentActivity(it) + } + }.apply { + mAdapterCommunity = this + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + val commentCount = data?.getIntExtra(CommentActivity.COMMENT_COUNT, 0) + if (commentCount != null && commentCount != 0) { + mViewModel.commentDetail?.reply = commentCount + mViewModel.commentCount = commentCount + mViewModel.load(LoadType.REFRESH) + mBinding.toolbarContainer.commentDialogCountTv.text = "${mViewModel.commentDetail?.reply}条回复" + } + } + + private fun initView() { + mSkeletonScreen = Skeleton.bind(skeletonView).shimmer(false).load(R.layout.fragment_article_detail_comment_skeleton).show() + + mBinding.inputContainer.bottomCommentIv.visibility = View.GONE + mBinding.inputContainer.bottomCommentTv.visibility = View.GONE + mBinding.inputContainer.bottomLikeIv.visibility = View.GONE + mBinding.inputContainer.bottomLikeTv.visibility = View.GONE + mBinding.inputContainer.bottomStarIv.visibility = View.GONE + mBinding.inputContainer.bottomStarTv.visibility = View.GONE + + mBinding.inputContainer.replyTv.setRoundedColorBackground(R.color.text_F0F0F0, 19F) + mBinding.inputContainer.replyTv.setDebouncedClickListener { + mViewModel.commentDetail?.let { startCommentActivity(it) } + } + mBinding.toolbarContainer.commentCloseIv.setOnClickListener { requireActivity().finish() } + } + + @SuppressLint("SetTextI18n") + private fun initObserver() { + mViewModel.loadResultLiveData.observeNonNull(this) { + when (it) { + BaseCommentViewModel.LoadResult.SUCCESS -> { + mReuseNoConn?.visibility = View.GONE + mListLoading?.visibility = View.GONE + + val showKeyboard = arguments?.getBoolean(EntranceUtils.KEY_SHOW_KEYBOARD_IF_NEEDED) + ?: false + if (showKeyboard) { + mBinding.inputContainer.replyTv.performClick() + } + + mBinding.inputContainer.replyTv.text = "回复:${mViewModel.commentDetail?.user?.name}" + mBinding.toolbarContainer.commentDialogCountTv.text = "${mViewModel.commentDetail?.reply}条回复" + AppExecutor.uiExecutor.executeWithDelay(Runnable { + if ((mAdapterCommunity?.entityList?.size ?: 0) > 2) { + mListRv.smoothScrollToPosition(1) + } + }, 100) + } + else -> { + if (it == BaseCommentViewModel.LoadResult.DELETED) { + mReuseNoConn?.visibility = View.GONE + mReuseNoData?.visibility = View.VISIBLE + toast(R.string.content_delete_toast) + } else { + mReuseNoConn?.visibility = View.VISIBLE + mReuseNoData?.visibility = View.GONE + mReuseNoConn?.setOnClickListener { + mViewModel.getComment() + mReuseNoConn?.visibility = View.GONE + showSkeleton(true) + } + } + + mListLoading?.visibility = View.GONE + mBinding.inputContainer.bottomContainer.visibility = View.GONE + mBinding.bottomShadowView.visibility = View.GONE + showSkeleton(false) + } + } + } + } + + override fun provideListViewModel(): CommentConversationViewModel { + return viewModelProvider( + CommentConversationViewModel.Factory( + HaloApp.getInstance().application, + arguments?.getString(EntranceUtils.KEY_COMMUNITY_ARTICLE_ID) ?: "", + arguments?.getString(CommentActivity.VIDEO_ID) ?: "", + arguments?.getString(CommentActivity.QUESTION_ID) ?: "", + arguments?.getString(EntranceUtils.KEY_COMMUNITY_ID) ?: "", + arguments?.getString(EntranceUtils.KEY_COMMENT_ID) ?: "")) + } + + override fun onBackPressed(): Boolean { + requireActivity().finish() + return super.onBackPressed() + } + + override fun onLoadDone() { + showSkeleton(false) + super.onLoadDone() + } + + private fun startCommentActivity(comment: CommentEntity) { + if (mViewModel.articleId.isNotEmpty()) { + val intent = CommentActivity.getArticleCommentReplyIntent( + requireContext(), + mViewModel.articleId, + mViewModel.commentDetail?.reply, + true, + comment.id ?: "", + comment, + true) + startActivityForResult(intent, CommentActivity.REQUEST_CODE) + } else if (mViewModel.videoId.isNotEmpty()) { + val intent = CommentActivity.getVideoCommentReplyIntent( + requireContext(), + mViewModel.videoId, + 0, + comment.user.id == UserManager.getInstance().userId, + true, true, comment + ) + startActivityForResult(intent, CommentActivity.REQUEST_CODE) + } else if (mViewModel.questionId.isNotEmpty()) { + val intent = CommentActivity.getQuestionCommentReplyIntent(requireContext(), + mViewModel.questionId, + "", + mViewModel.commentCount, + true, + true, + comment) + startActivityForResult(intent, CommentActivity.REQUEST_CODE) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt new file mode 100644 index 0000000000..849c7fb911 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt @@ -0,0 +1,102 @@ +package com.gh.gamecenter.qa.comment.conversation + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel +import com.gh.gamecenter.qa.article.detail.CommentItemData +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.RetrofitManager +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.schedulers.Schedulers +import retrofit2.HttpException + +class CommentConversationViewModel(application: Application, + articleId: String = "", + videoId: String = "", + questionId: String = "", + communityId: String = "", + var commentId: String = "") + : BaseCommentViewModel(application, articleId, videoId, questionId, communityId) { + var commentDetail: CommentEntity? = null + var positionInOriginList = -1 + + override fun provideDataObservable(page: Int): Observable>? = null + + override fun provideDataSingle(page: Int): Single>? { + return when { + articleId.isNotEmpty() -> { + mApi.getCommunityArticleCommentReply(communityId, articleId, commentId, currentSortType.value, page) + } + videoId.isNotEmpty() -> { + mApi.getVideoCommentReply(videoId, commentId, currentSortType.value, page) + } + questionId.isNotEmpty() -> { + mApi.getQuestionCommentReply(questionId, commentId, currentSortType.value, page) + } + else -> null + } + } + + @SuppressLint("CheckResult") + fun getComment() { + val single = when { + articleId.isNotEmpty() -> { + mApi.getCommunityArticleComment(communityId, articleId, commentId) + } + videoId.isNotEmpty() -> { + mApi.getCommunityVideoComment(videoId, commentId) + } + questionId.isNotEmpty() -> { + mApi.getCommunityQuestionComment(questionId, commentId) + } + else -> null + } ?: return + single.subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + @SuppressLint("CheckResult") + override fun onSuccess(data: CommentEntity) { + commentDetail = data + commentDetail?.floor = positionInOriginList + commentCount = data.reply + topItemData = CommentItemData(commentTop = data) + loadResultLiveData.postValue(LoadResult.SUCCESS) + mergeListData(mListLiveData.value, hasFilter = false) + } + + override fun onFailure(exception: Exception) { + if (exception is HttpException && exception.code().toString().contains("404")) { + loadResultLiveData.postValue(LoadResult.DELETED) + } else { + loadResultLiveData.postValue(LoadResult.NETWORK_ERROR) + } + } + }) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mergeListData(it, hasFilter = false) } + } + + + class Factory(private val application: Application, + private val articleId: String = "", + private val videoId: String = "", + private val questionId: String = "", + private val communityId: String = "", + private val commentId: String) : ViewModelProvider.NewInstanceFactory() { + + override fun create(modelClass: Class): T { + return CommentConversationViewModel( + application = application, + articleId = articleId, + videoId = videoId, + questionId = questionId, + communityId = communityId, + commentId = commentId) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseActivityDialogFragment.kt similarity index 54% rename from app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumDialogFragment.kt rename to app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseActivityDialogFragment.kt index 492b4e543b..423645f9d1 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumDialogFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseActivityDialogFragment.kt @@ -2,46 +2,53 @@ package com.gh.gamecenter.qa.dialog import android.app.Dialog import android.os.Bundle +import android.os.ProxyFileDescriptorCallback import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf import androidx.fragment.app.FragmentTransaction import com.gh.base.fragment.BaseDialogFragment import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.DialogChooseActivityBinding import com.gh.gamecenter.databinding.DialogChooseForumBinding -import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.forum.select.ForumSelectFragment +import com.gh.gamecenter.entity.ActivityLabelEntity +import com.gh.gamecenter.video.label.VideoLabelFragment import com.halo.assistant.HaloApp -class ChooseForumDialogFragment : BaseDialogFragment() { - private lateinit var binding: DialogChooseForumBinding - var onSelectCallback: ((entity: CommunityEntity) -> Unit)? = null +class ChooseActivityDialogFragment : BaseDialogFragment() { + private lateinit var binding: DialogChooseActivityBinding + var onSelectCallback: ((entity: ActivityLabelEntity) -> Unit)? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - binding = DialogChooseForumBinding.inflate(inflater, container, false) + binding = DialogChooseActivityBinding.inflate(inflater, null, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val beginTransaction = childFragmentManager.beginTransaction() - val forumSelectFragment = childFragmentManager.findFragmentByTag(ForumSelectFragment::class.java.simpleName) as? ForumSelectFragment - ?: ForumSelectFragment() - forumSelectFragment.onSelectCallback = { - onSelectCallback?.invoke(it) + val fragment = childFragmentManager.findFragmentByTag(VideoLabelFragment::class.java.simpleName) as? VideoLabelFragment + ?: VideoLabelFragment() + fragment.arguments = arguments + beginTransaction.replace(binding.container.id, fragment, VideoLabelFragment::class.java.simpleName) + beginTransaction.commitAllowingStateLoss() + binding.confirmTv.setOnClickListener { + val activityLabelEntity = fragment.getSelectedActivity() + if (activityLabelEntity == null) { + toast("请选择活动标签") + return@setOnClickListener + } + onSelectCallback?.invoke(activityLabelEntity) dismissAllowingStateLoss() } - beginTransaction.replace(binding.container.id, forumSelectFragment, ForumSelectFragment::class.java.simpleName) - beginTransaction.commitAllowingStateLoss() - binding.closeIv.setOnClickListener { dismissAllowingStateLoss() } } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val createDialog = super.onCreateDialog(savedInstanceState) createDialog.setCanceledOnTouchOutside(true) - val window = createDialog.window window?.setGravity(Gravity.BOTTOM) window?.setWindowAnimations(R.style.community_publication_animation) @@ -55,20 +62,22 @@ class ChooseForumDialogFragment : BaseDialogFragment() { dialog?.window?.setLayout(width, height) } - companion object { - fun show(activity: AppCompatActivity, onSelectCallback: ((entity: CommunityEntity) -> Unit)) { - var fragment = activity.supportFragmentManager.findFragmentByTag(ChooseForumDialogFragment::class.java.name) as? ChooseForumDialogFragment + const val TAG_ACTIVITY_ID = "tagActivityId" + + fun show(activity: AppCompatActivity, tagActivityId: String? = "", callback: (entity: ActivityLabelEntity) -> Unit) { + var fragment = activity.supportFragmentManager.findFragmentByTag(ChooseActivityDialogFragment::class.java.name) as? ChooseActivityDialogFragment if (fragment == null) { - fragment = ChooseForumDialogFragment() - fragment.onSelectCallback = onSelectCallback - fragment.show(activity.supportFragmentManager, ChooseForumDialogFragment::class.java.name) + fragment = ChooseActivityDialogFragment().also { it.arguments = bundleOf(TAG_ACTIVITY_ID to tagActivityId) } + fragment.onSelectCallback = callback + fragment.show(activity.supportFragmentManager, ChooseActivityDialogFragment::class.java.name) } else { - fragment.onSelectCallback = onSelectCallback val transaction: FragmentTransaction = activity.supportFragmentManager.beginTransaction() + fragment.onSelectCallback = callback transaction.show(fragment) transaction.commit() } } } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt new file mode 100644 index 0000000000..98cc78838b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt @@ -0,0 +1,113 @@ +package com.gh.gamecenter.qa.dialog + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.core.widget.doOnTextChanged +import androidx.fragment.app.Fragment +import com.gh.base.BaseActivity +import com.gh.base.adapter.FragmentAdapter +import com.gh.common.util.DisplayUtils +import com.gh.common.util.EntranceUtils +import com.gh.common.util.doOnEnd +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.DialogChooseForumBinding +import com.gh.gamecenter.entity.CommunityEntity + +class ChooseForumActivity : BaseActivity() { + private lateinit var binding: DialogChooseForumBinding + private var mSearchResultFragment: ChooseForumContainerFragment? = null + private val mTabTitleList = arrayListOf("我的关注", "热门论坛") + private val mFragmentsList: ArrayList = arrayListOf() + + override fun getLayoutId(): Int = R.layout.dialog_choose_forum + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + DisplayUtils.transparentStatusBar(this) + binding = DialogChooseForumBinding.bind(mContentView) + + initViewPager() + binding.searchEt.doOnTextChanged { _, _, _, _ -> + if (binding.searchEt.text.isNullOrEmpty()) { + switchUI(false) + } else { + switchUI(true) + if (mSearchResultFragment != null && mSearchResultFragment?.isAdded == true) { + mSearchResultFragment?.setSearchKey(binding.searchEt.text.toString()) + } + } + } + binding.closeIv.setOnClickListener { finish() } + binding.maskView.alpha = 0f + binding.maskView.animate().alpha(1f).setDuration(300).start() + binding.maskView.setOnClickListener { binding.closeIv.performClick() } + binding.forumContainer.translationY = (DisplayUtils.getScreenHeight()).toFloat() + binding.forumContainer.animate().translationY(0f).setDuration(300).start() + } + + private fun initViewPager() { + mFragmentsList.clear() + mFragmentsList.add(ChooseForumContainerFragment.getInstance(ChooseForumContainerFragment.ChooseForumType.ATTENTION)) + mFragmentsList.add(ChooseForumContainerFragment.getInstance(ChooseForumContainerFragment.ChooseForumType.HOT)) + binding.viewPager.offscreenPageLimit = mFragmentsList.size + binding.viewPager.adapter = FragmentAdapter(supportFragmentManager, mFragmentsList, mTabTitleList) + binding.tabLayout.setupWithViewPager(binding.viewPager) + binding.tabIndicator.setupWithTabLayout(binding.tabLayout) + binding.tabIndicator.setupWithViewPager(binding.viewPager) + binding.tabIndicator.setIndicatorWidth(20) + } + + private fun switchUI(isSearch: Boolean) { + if (isSearch) { + binding.tabContainer.visibility = View.GONE + binding.line.visibility = View.GONE + binding.viewPager.visibility = View.GONE + binding.searchResultContainer.visibility = View.VISIBLE + + if (mSearchResultFragment == null || mSearchResultFragment?.isAdded == false) { + val beginTransaction = supportFragmentManager.beginTransaction() + mSearchResultFragment = supportFragmentManager.findFragmentByTag(ChooseForumContainerFragment::class.java.simpleName) as? ChooseForumContainerFragment + ?: ChooseForumContainerFragment.getInstance(ChooseForumContainerFragment.ChooseForumType.SEARCH, binding.searchEt.text.toString()) as ChooseForumContainerFragment + beginTransaction.replace(binding.searchResultContainer.id, mSearchResultFragment!!, ChooseForumContainerFragment::class.java.simpleName) + beginTransaction.commitAllowingStateLoss() + } + } else { + binding.tabContainer.visibility = View.VISIBLE + binding.line.visibility = View.VISIBLE + binding.viewPager.visibility = View.VISIBLE + binding.searchResultContainer.visibility = View.GONE + } + } + + fun chooseSuccess(community: CommunityEntity) { + val intent = Intent() + intent.putExtra(EntranceUtils.KEY_COMMUNITY_DATA, community) + setResult(Activity.RESULT_OK, intent) + finish() + } + + override fun finish() { + overridePendingTransition(0, 0) + binding.maskView.animate().alpha(0f).setDuration(300).start() + binding.root.animate() + .translationY(DisplayUtils.getScreenHeight().toFloat()) + .setDuration(300) + .doOnEnd { + super.finish() + } + .start() + } + + companion object { + const val CHOOSE_FORUM_REQUEST = 10 + + fun startChooseForumActivity(context: Activity) { + val intent = Intent(context, ChooseForumActivity::class.java) + context.overridePendingTransition(0, 0) + context.startActivityForResult(intent, CHOOSE_FORUM_REQUEST) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerAdapter.kt new file mode 100644 index 0000000000..91b96d2995 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerAdapter.kt @@ -0,0 +1,41 @@ +package com.gh.gamecenter.qa.dialog + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.HtmlUtils +import com.gh.common.util.MtaHelper +import com.gh.common.util.goneIf +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.ForumItemBinding +import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.forum.detail.ForumDetailActivity +import com.gh.gamecenter.qa.entity.CommunitySelectEntity + +class ChooseForumContainerAdapter(content: Context, val onSelectCallback: ((entity: CommunityEntity) -> Unit)? = null) : ListAdapter(content) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return ForumItemViewHolder(ForumItemBinding.inflate(LayoutInflater.from(mContext), parent, false)) + } + + override fun getItemCount(): Int = mEntityList.size + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is ForumItemViewHolder) { + val forumEntity = mEntityList[position] + holder.binding.entity = forumEntity + holder.binding.forumIcon.displayGameIcon(forumEntity.game.getIcon(), forumEntity.game.iconSubscript) + holder.binding.followTv.visibility = View.GONE + holder.itemView.setOnClickListener { + onSelectCallback?.invoke(CommunityEntity(forumEntity.id, HtmlUtils.stripHtml(forumEntity.name), + game = forumEntity.game, icon = forumEntity.game.getIcon(), iconSubscript = forumEntity.game.iconSubscript)) + } + } + } + + class ForumItemViewHolder(val binding: ForumItemBinding) : BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerFragment.kt new file mode 100644 index 0000000000..012e2cbff8 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerFragment.kt @@ -0,0 +1,65 @@ +package com.gh.gamecenter.qa.dialog + +import android.os.Bundle +import android.view.View +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.EntranceUtils +import com.gh.common.util.viewModelProvider +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.ForumEntity + +class ChooseForumContainerFragment : ListFragment() { + + private var mAdapter: ChooseForumContainerAdapter? = null + private var type: String = "" + private var searchKey: String = "" + + override fun onCreate(savedInstanceState: Bundle?) { + type = arguments?.getString(EntranceUtils.KEY_CHOOSE_FORUM_TYPE) ?: "" + searchKey = arguments?.getString(EntranceUtils.KEY_SEARCHKEY) ?: "" + super.onCreate(savedInstanceState) + } + + override fun shouldLoadMore(): Boolean = false + + override fun provideListAdapter(): ListAdapter<*> { + return mAdapter ?: ChooseForumContainerAdapter(requireContext()) { + if (requireActivity() is ChooseForumActivity) { + (requireActivity() as ChooseForumActivity).chooseSuccess(it) + } + }.apply { + mAdapter = this + } + } + + override fun provideListViewModel(): ChooseForumContainerViewModel { + return viewModelProvider(ChooseForumContainerViewModel.Factory(type, searchKey)) + } + + fun setSearchKey(searchKey: String) { + mListViewModel?.setSearchKeyAndRefresh(searchKey) + } + + override fun isAutomaticLoad(): Boolean = type != ChooseForumType.SEARCH.value + + override fun getItemDecoration(): RecyclerView.ItemDecoration? = null + + companion object { + fun getInstance(type: ChooseForumType, searchKey: String = ""): Fragment { + val bundle = bundleOf(EntranceUtils.KEY_CHOOSE_FORUM_TYPE to type.value, EntranceUtils.KEY_SEARCHKEY to searchKey) + return ChooseForumContainerFragment().apply { + arguments = bundle + } + } + } + + enum class ChooseForumType(val value: String) { + ATTENTION("attention"), + HOT("hot"), + SEARCH("search") + } +} diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerViewModel.kt new file mode 100644 index 0000000000..d24c8313fb --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerViewModel.kt @@ -0,0 +1,52 @@ +package com.gh.gamecenter.qa.dialog + +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.common.exposure.ExposureSource +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.category2.CategoryV2ListViewModel +import com.gh.gamecenter.entity.ForumEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.retrofit.service.ApiService +import com.halo.assistant.HaloApp +import io.reactivex.Observable + +class ChooseForumContainerViewModel(application: Application, val type: String, var searchKey: String) : ListViewModel(application) { + private val mApi: ApiService = RetrofitManager.getInstance(getApplication()).api + init { + if (searchKey.isNotEmpty()) { + load(LoadType.REFRESH) + } + } + override fun provideDataObservable(page: Int): Observable> { + return when (type) { + ChooseForumContainerFragment.ChooseForumType.ATTENTION.value -> { + mApi.getFollowsForum(UserManager.getInstance().userId) + } + ChooseForumContainerFragment.ChooseForumType.HOT.value -> { + mApi.hotForum + } + else -> { + mApi.searchBbs(searchKey, page) + } + } + } + + fun setSearchKeyAndRefresh(searchKey: String) { + this.searchKey = searchKey + load(LoadType.REFRESH) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource>(mListLiveData) { mResultLiveData.postValue(it) } + } + + class Factory(val type: String, val searchKey: String) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return ChooseForumContainerViewModel(HaloApp.getInstance().application, type, searchKey) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/MoreFunctionPanelDialog.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/MoreFunctionPanelDialog.kt index b31c89b478..44e5322704 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/dialog/MoreFunctionPanelDialog.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/MoreFunctionPanelDialog.kt @@ -11,7 +11,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentTransaction import com.gh.common.dialog.BaseDraggableDialogFragment -import com.gh.common.util.MtaHelper import com.gh.common.util.ShareUtils import com.gh.common.util.dip2px import com.gh.gamecenter.R @@ -23,10 +22,15 @@ class MoreFunctionPanelDialog : BaseDraggableDialogFragment() { private lateinit var binding: DialogGameDetailMoreBinding var menuItems: ArrayList = arrayListOf() var title: String = "" + var shareUrl: String = "" var shareUtils: ShareUtils? = null var onItemClickCallback: ((menuItem: MenuItemEntity) -> Unit)? = null - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { binding = DialogGameDetailMoreBinding.inflate(inflater, container, false) return binding.root } @@ -39,37 +43,14 @@ class MoreFunctionPanelDialog : BaseDraggableDialogFragment() { binding.feedbackTv.visibility = View.GONE binding.copyrightTv.visibility = View.GONE - binding.shareWechatTv.setOnClickListener { - shareUtils?.wechatShare() - MtaHelper.onEvent("内容分享", "微信好友", shareUtils?.title) - } - binding.sharePyquanTv.setOnClickListener { - shareUtils?.wechatMomentsShare() - MtaHelper.onEvent("内容分享", "微信朋友圈", shareUtils?.title) - } - binding.shareQqTv.setOnClickListener { - shareUtils?.qqShare() - MtaHelper.onEvent("内容分享", "QQ好友", shareUtils?.title) - } - binding.shareQqzoneTv.setOnClickListener { - shareUtils?.qZoneShare() - MtaHelper.onEvent("内容分享", "QQ空间", shareUtils?.title) - } - binding.shareWeiboTv.setOnClickListener { - shareUtils?.sinaWeiboShare() - MtaHelper.onEvent("内容分享", "新浪微博", shareUtils?.title) - } - binding.shareSmsTv.setOnClickListener { - shareUtils?.shortMessageShare() - MtaHelper.onEvent("内容分享", "短信", shareUtils?.title) - } - binding.copyLinkTv.setOnClickListener { - shareUtils?.copyLink(shareUtils?.shareUrl) - MtaHelper.onEvent("内容分享", "复制链接", shareUtils?.title) - } - binding.cancelTv.setOnClickListener { - dismissAllowingStateLoss() - } + binding.shareWechatTv.setOnClickListener { shareUtils?.wechatShare() } + binding.sharePyquanTv.setOnClickListener { shareUtils?.wechatMomentsShare() } + binding.shareQqTv.setOnClickListener { shareUtils?.qqShare() } + binding.shareQqzoneTv.setOnClickListener { shareUtils?.qZoneShare() } + binding.shareWeiboTv.setOnClickListener { shareUtils?.sinaWeiboShare() } + binding.shareSmsTv.setOnClickListener { shareUtils?.shortMessageShare() } + binding.copyLinkTv.setOnClickListener { shareUtils?.copyLink(shareUrl) } + binding.cancelTv.setOnClickListener { dismissAllowingStateLoss() } addActionItem() } @@ -86,15 +67,15 @@ class MoreFunctionPanelDialog : BaseDraggableDialogFragment() { private fun createItemView(itemEntity: MenuItemEntity): View { val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) - params.leftMargin = 16f.dip2px() + params.leftMargin = 16F.dip2px() return TextView(requireContext()).apply { - textSize = 11f + textSize = 11F text = itemEntity.text setTextColor(ContextCompat.getColor(requireContext(), if (itemEntity.isEnable) R.color.text_666666 else R.color.text_999999)) includeFontPadding = false gravity = Gravity.CENTER layoutParams = params - compoundDrawablePadding = 8f.dip2px() + compoundDrawablePadding = 8F.dip2px() setCompoundDrawablesWithIntrinsicBounds(null, ContextCompat.getDrawable(requireContext(), itemEntity.normalIcon), null, null) } } diff --git a/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt index 2d2cde3ad3..74272d938f 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt @@ -6,8 +6,8 @@ import android.os.Bundle import androidx.fragment.app.Fragment import com.gh.base.BaseActivity_TabLayout import com.gh.gamecenter.qa.article.draft.ArticleDraftFragment -import com.gh.gamecenter.qa.answer.draft.AnswerDraftFragment import com.gh.gamecenter.qa.questions.draft.QuestionDraftFragment +import com.gh.gamecenter.video.videomanager.VideoDraftFragment class CommunityDraftWrapperActivity : BaseActivity_TabLayout() { @@ -19,13 +19,13 @@ class CommunityDraftWrapperActivity : BaseActivity_TabLayout() { override fun initFragmentList(fragments: MutableList) { fragments.add(ArticleDraftFragment()) fragments.add(QuestionDraftFragment()) - fragments.add(AnswerDraftFragment()) + fragments.add(VideoDraftFragment()) } override fun initTabTitleList(tabTitleList: MutableList) { tabTitleList.add("帖子草稿") tabTitleList.add("问题草稿") - tabTitleList.add("回答草稿") + tabTitleList.add("视频草稿") } companion object { diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/FullScreenVideoActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/FullScreenVideoActivity.kt new file mode 100644 index 0000000000..6ffb699aaf --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/FullScreenVideoActivity.kt @@ -0,0 +1,75 @@ +package com.gh.gamecenter.qa.editor + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.base.BaseActivity +import com.gh.common.util.DisplayUtils +import com.gh.common.util.EntranceUtils +import com.gh.common.util.NetworkUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.ActivityFullScreenVideoBinding +import com.gh.gamecenter.video.detail.CustomManager +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder + +class FullScreenVideoActivity : BaseActivity() { + lateinit var mBinding: ActivityFullScreenVideoBinding + + override fun getLayoutId(): Int = R.layout.activity_full_screen_video + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + DisplayUtils.transparentStatusBar(this) + mBinding = ActivityFullScreenVideoBinding.bind(mContentView) + + val url = intent.getStringExtra(EntranceUtils.KEY_URL) ?: "" + val poster = intent.getStringExtra(EntranceUtils.KEY_POSTER_PATH) ?: "" + mBinding.videoView.backBtn.setOnClickListener { finish() } + setUpTopVideo(url, poster) + } + + private fun setUpTopVideo(url: String, poster: String) { + GSYVideoOptionBuilder() + .setIsTouchWigetFull(false) + .setIsTouchWiget(false) + .setRotateViewAuto(false) + .setShowFullAnimation(false) + .setSeekRatio(1f) + .setUrl(url) + .setCacheWithPlay(true) + .build(mBinding.videoView) + + mBinding.videoView.updateThumb(poster) + + if (NetworkUtils.isWifiConnected(this)) { + mBinding.videoView.startPlayLogic(isAutoPlay = true) + } + mBinding.videoView.observeVolume(this) + } + + override fun onResume() { + super.onResume() + CustomManager.onResume(mBinding.videoView.getKey()) + } + + override fun onPause() { + super.onPause() + CustomManager.onPause(mBinding.videoView.getKey()) + } + + override fun onDestroy() { + super.onDestroy() + CustomManager.releaseAllVideos(mBinding.videoView.getKey()) + mBinding.videoView.disposableTimer() + } + + companion object { + fun start(context: Context, url: String, poster: String) { + val intent = Intent(context, FullScreenVideoActivity::class.java) + intent.putExtra(EntranceUtils.KEY_URL, url) + intent.putExtra(EntranceUtils.KEY_POSTER_PATH, poster) + context.startActivity(intent) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/FullScreenVideoView.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/FullScreenVideoView.kt new file mode 100644 index 0000000000..28770174f0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/FullScreenVideoView.kt @@ -0,0 +1,415 @@ +package com.gh.gamecenter.qa.editor + +import android.app.Activity +import android.app.Application +import android.content.Context +import android.os.Bundle +import android.os.Handler +import android.util.AttributeSet +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.Surface +import android.view.View +import android.widget.ImageView +import android.widget.SeekBar +import androidx.core.content.ContextCompat +import com.facebook.drawee.view.SimpleDraweeView +import com.gh.common.constant.Constants +import com.gh.common.observer.MuteCallback +import com.gh.common.observer.VolumeObserver +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.video.detail.CustomManager +import com.lightgame.utils.Utils +import com.shuyu.gsyvideoplayer.utils.CommonUtil +import com.shuyu.gsyvideoplayer.utils.NetworkUtils +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge +import io.reactivex.disposables.Disposable +import kotlinx.android.synthetic.main.layout_game_detail_video_portrait.view.* +import kotlinx.android.synthetic.main.piece_video_control.view.* +import kotlinx.android.synthetic.main.piece_video_replay.view.* +import java.util.* + +class FullScreenVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : StandardGSYVideoPlayer(context, attrs) { + private var mMuteCallback: MuteCallback + private var mVolumeObserver: VolumeObserver + private var mMuteDisposable: Disposable? = null + private var mIsAutoPlay = false + private var mVideoIsMuted = false + val backBtn = findViewById(R.id.back) + var uuid = UUID.randomUUID().toString() + var thumbImage: SimpleDraweeView = findViewById(R.id.thumbImage) + + init { + post { + gestureDetector = GestureDetector(getContext().applicationContext, object : GestureDetector.SimpleOnGestureListener() { + override fun onSingleTapConfirmed(e: MotionEvent): Boolean { + if (!mChangePosition && !mChangeVolume && !mBrightness) { + onClickUiToggle() + } + return super.onSingleTapConfirmed(e) + } + }) + + showBackBtn() + + volume.setOnClickListener { toggleMute() } + } + mFullscreenButton.visibility = View.GONE + + mMuteCallback = object : MuteCallback { + override fun onMute(isMute: Boolean) { + if (isMute) { + mute() + } else { + unMute() + } + } + } + + mVolumeObserver = VolumeObserver(context, Handler(), mMuteCallback) + + setBackFromFullScreenListener { + clearFullscreenLayout() + } + + errorBtn?.setOnClickListener { + debounceActionWithInterval(errorBtn.id, 1000) { + if (!NetworkUtils.isAvailable(mContext)) { + Utils.toast(context, "网络异常,请检查手机网络状态") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + return@debounceActionWithInterval + } + startPlayLogic(false) + } + } + } + + //这个必须配置最上面的构造才能生效 + override fun getLayoutId(): Int { + return R.layout.layout_full_screen_detail_video_portrait + } + + fun observeVolume(activity: Activity?) { + tryWithDefaultCatch { + activity?.applicationContext?.contentResolver?.registerContentObserver( + android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver) + + activity?.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks { + override fun onActivityPaused(activity: Activity) { + activity.applicationContext?.contentResolver?.unregisterContentObserver(mVolumeObserver) + activity.unregisterActivityLifecycleCallbacks(this) + } + + override fun onActivityStarted(activity: Activity) {} + override fun onActivityDestroyed(activity: Activity) {} + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + override fun onActivityStopped(activity: Activity) {} + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} + override fun onActivityResumed(activity: Activity) {} + }) + } + } + + fun startPlayLogic(isAutoPlay: Boolean) { + mIsAutoPlay = isAutoPlay +// violenceUpdateMuteStatus() + startPlayLogic() + } + + fun violenceUpdateMuteStatus() { + if (mMuteDisposable != null) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + mMuteDisposable = rxTimer(25) { + if (it >= 400) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + updateMuteStatus() + } + } + + fun disposableTimer() { + if (mMuteDisposable != null && !mMuteDisposable!!.isDisposed) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + } + + private fun toggleMute() { + if (mVideoIsMuted) { + unMute(true) + } else { + mute(true) + } + } + + fun updateMuteStatus() { + if (!mVideoIsMuted) { + mute() + } else { + unMute() + } + } + + private fun mute(isManual: Boolean = false) { + mVideoIsMuted = true + volume.setImageResource(R.drawable.ic_game_detail_volume_off) + CustomManager.getCustomManager(getKey()).isNeedMute = true + SPUtils.setBoolean(Constants.SP_TOP_VIDEO_VOICE, true) + if (isManual) { + Utils.toast(context, "当前处于静音状态") + uploadVideoStreamingPlaying("点击静音") + } + } + + private fun unMute(isManual: Boolean = false) { + mVideoIsMuted = false + volume.setImageResource(R.drawable.ic_game_detail_volume_on) + CustomManager.getCustomManager(getKey()).isNeedMute = false + SPUtils.setBoolean(Constants.SP_TOP_VIDEO_VOICE, false) + if (isManual) { + uploadVideoStreamingPlaying("取消静音") + } + } + + // 重载以减少横竖屏切换的时间 + override fun checkoutState() { + removeCallbacks(mCheckoutTask) + postDelayed(mCheckoutTask, 300) + } + + override fun clearFullscreenLayout() { + super.clearFullscreenLayout() + updateMuteStatus() + hideBackBtn() + } + + fun updateThumb(url: String) { + ImageUtils.display(thumbImage, url) + } + + override fun touchDoubleUp() { + // we do not need double click to play or pause + } + + // 不需要弹弹窗,直接播放 + override fun showWifiDialog() { + startPlayLogic(false) + //val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false) + //if (trafficVideo) { + // 不延迟的话 isCacheFile 可能直接返回 false + postDelayed({ + // 这个库的 NetworkUtils.isWifiConnected 可能会触发空指针,这里换为我们自己的 + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "网络异常,请检查手机网络状态") + } else if (!com.gh.common.util.NetworkUtils.isWifiConnected(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "当前为非Wi-Fi环境,请注意流量消耗") + } + }, 100) + // } + } + + override fun getGSYVideoManager(): GSYVideoViewBridge { + CustomManager.getCustomManager(getKey()).initContext(context.applicationContext) + return CustomManager.getCustomManager(getKey()) + } + + fun getKey(): String { + return uuid + } + + override fun onAutoCompletion() { + + //播放完成后判断是否已缓冲完毕,没有完成显示播放错误状态 + if (mBufferPoint != 0 && mBufferPoint != 100 && isShown) { + gsyVideoManager.releaseMediaPlayer() + changeUiToPreparingShow() + postDelayed({ + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext)) { + Utils.toast(context, "网络错误,视频播放失败") + changeUiToError() + } + }, 10 * 1000) + } + uploadVideoStreamingPlaying("播放完毕") + super.onAutoCompletion() + } + + override fun onStopTrackingTouch(seekBar: SeekBar?) { + super.onStopTrackingTouch(seekBar) + uploadVideoStreamingPlaying("拖动") + } + + override fun isShowNetConfirm(): Boolean { + return (!mOriginUrl.startsWith("file") && !mOriginUrl.startsWith("android.resource") && !CommonUtil.isWifiConnected(context) + && mNeedShowWifiTip) + } + + override fun updateStartImage() { + if (mStartButton is ImageView) { + val imageView = mStartButton as ImageView + when (mCurrentState) { + GSYVideoView.CURRENT_STATE_PLAYING -> imageView.setImageResource(R.drawable.ic_game_detail_pause) + GSYVideoView.CURRENT_STATE_ERROR -> imageView.setImageResource(R.drawable.ic_game_detail_play) + else -> imageView.setImageResource(R.drawable.ic_game_detail_play) + } + } + } + + override fun setStateAndUi(state: Int) { + super.setStateAndUi(state) + + if (state == CURRENT_STATE_AUTO_COMPLETE) { + hideAllWidget() + replayContainer.visibility = View.VISIBLE + mTopContainer.visibility = View.VISIBLE + replayIv.setOnClickListener { + startButton.performClick() +// violenceUpdateMuteStatus() + uploadVideoStreamingPlaying("重新播放") + } + } else { + replayContainer.visibility = View.GONE + } + } + + private fun showBackBtn() { + mTopContainer.background = ContextCompat.getDrawable(context, R.drawable.video_title_bg) + back.visibility = View.VISIBLE + } + + private fun hideBackBtn() { + mTopContainer?.setBackgroundResource(0) + back.visibility = View.GONE + } + + override fun getEnlargeImageRes(): Int { + return R.drawable.ic_game_detail_enter_full_screen + } + + override fun getShrinkImageRes(): Int { + return R.drawable.ic_game_detail_exit_full_screen + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (!isIfCurrentIsFullscreen) { + parent.requestDisallowInterceptTouchEvent(true) + } + return super.onInterceptTouchEvent(ev) + } + + /******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/ + + override fun onSurfaceUpdated(surface: Surface) { + super.onSurfaceUpdated(surface) + if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) { + mThumbImageViewLayout.visibility = View.INVISIBLE + uploadVideoStreamingPlaying("开始播放") + } + } + + override fun setViewShowState(view: View?, visibility: Int) { + if (view === mThumbImageViewLayout && visibility != View.VISIBLE) { + return + } + super.setViewShowState(view, visibility) + } + + /********************************各类UI的状态显示*********************************************/ + + override fun changeUiToNormal() { + super.changeUiToNormal() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPreparingShow() { + super.changeUiToPreparingShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPlayingShow() { + super.changeUiToPlayingShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPauseShow() { + super.changeUiToPauseShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToCompleteShow() { + super.changeUiToCompleteShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToError() { + super.changeUiToError() + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + override fun netWorkErrorLogic() { + super.netWorkErrorLogic() + Utils.toast(context, "网络错误,视频播放失败") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + //监控播放错误 + override fun onError(what: Int, extra: Int) { + super.onError(what, extra) + Utils.toast(context, "网络错误,视频播放失败") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + override fun releaseVideos() { + uploadVideoStreamingPlaying("结束播放") + CustomManager.releaseAllVideos(getKey()) + } + + override fun onVideoPause() { + super.onVideoPause() + uploadVideoStreamingPlaying("暂停播放") + } + + fun getCurrentPosition(): Long { + return mCurrentPosition + } + + fun uploadVideoStreamingPlaying(action: String) { + /*if (video == null || video?.url.isNullOrEmpty()) return + runOnIoThread { + val isLandscape = mOrientationUtils != null + val videoPlayModel = if (!isLandscape) { + if (mIsAutoPlay) "自动播放" else "点击播放" + } else "全屏播放" + val videoPlayTs = currentPositionWhenPlaying / 1000 + val videoTotalTime = duration / 1000 + val progress = if (videoTotalTime != 0) videoPlayTs.toFloat() / videoTotalTime.toFloat() * 100 else 0f + if (mContentLength == 0.0) { + mContentLength = ExoCacheManager.getContentLength(video!!.url) / 1024.0 / 1024.0 + } + + //https://exoplayer.dev/hello-world.html#a-note-on-threading + runOnUiThread { + LogUtils.uploadTopVideoStreamingPlaying(action, video?.videoId, video?.title, viewModel?.game?.id, viewModel?.game?.name, + videoPlayModel, videoPlayStatus(), mContentLength, videoTotalTime, videoPlayTs, progress.toInt()) + } + }*/ + } + + private fun videoPlayStatus(): String { + return when (mCurrentState) { + CURRENT_STATE_PLAYING, CURRENT_STATE_PREPAREING, CURRENT_STATE_PLAYING_BUFFERING_START -> "play" + GSYVideoView.CURRENT_STATE_PAUSE -> "pause" + else -> "" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaActivity.kt new file mode 100644 index 0000000000..4fba2d9499 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaActivity.kt @@ -0,0 +1,129 @@ +package com.gh.gamecenter.qa.editor + +import android.content.Context +import android.content.Intent +import android.database.Cursor +import android.os.Bundle +import android.view.View +import android.widget.AdapterView +import android.widget.ImageView +import android.widget.PopupWindow +import androidx.core.content.ContextCompat +import com.gh.base.ToolBarActivity +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.R +import com.halo.assistant.HaloApp +import com.zhihu.matisse.Matisse +import com.zhihu.matisse.MimeType +import com.zhihu.matisse.internal.entity.Album +import com.zhihu.matisse.internal.entity.SelectionSpec +import com.zhihu.matisse.internal.model.AlbumCollection + +class LocalMediaActivity : ToolBarActivity(), AlbumCollection.AlbumCallbacks { + private var mLocalMediaFragment: LocalMediaFragment? = null + private lateinit var mAlbumsSpinner: VideoAlbumsSpanner + private lateinit var mAlbumsAdapter: VideoAlbumsAdapter + private val mAlbumCollection = AlbumCollection() + private var mIsFirstAlbumLoad = true + private var mChooseType = "" + + override fun getLayoutId(): Int = R.layout.activity_video_tablayout_viewpager + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mChooseType = intent.getStringExtra(EntranceUtils.KEY_TYPE) ?: "" + if (mChooseType == ChooseType.VIDEO.value) { + setNavigationTitle("本地视频") + } else { + setNavigationTitle("本地图片") + } + mLocalMediaFragment = LocalMediaFragment().apply { arguments = intent.extras } + supportFragmentManager.beginTransaction().replace(R.id.container, mLocalMediaFragment!!, LocalMediaFragment::class.java.name).commitAllowingStateLoss() + initAlbumsSpinner() + mTitleTv.setOnClickListener { + mAlbumsSpinner.show(findViewById(R.id.container).height) + setPhotoNavigationTitle(true) + } + } + + private fun initAlbumsSpinner() { + mAlbumsSpinner = VideoAlbumsSpanner(this) + mAlbumsAdapter = VideoAlbumsAdapter(this) + + mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.normal_toolbar)) + mAlbumsSpinner.setAdapter(mAlbumsAdapter) + mAlbumsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + mAlbumCollection.setStateCurrentSelection(position) + mAlbumsAdapter.cursor.moveToPosition(position) + val album = Album.valueOf(mAlbumsAdapter.cursor) + if (album.isAll && SelectionSpec.getInstance().capture) { + album.addCaptureCount() + } + if (mLocalMediaFragment?.isAdded == true) { + mLocalMediaFragment?.loadVideos(album) + } + } + } + + mAlbumsSpinner.setDismissListener(PopupWindow.OnDismissListener { + setPhotoNavigationTitle(false) + }) + //必须加这行代码,[SelectionSpec]是单例模式,下次使用必须先更新MimeType + val mimeType = if (mChooseType == ChooseType.VIDEO.value) { + MimeType.ofVideo() + } else { + MimeType.ofImage() + } + Matisse.from(this).choose(mimeType).showSingleMediaType(true) + mAlbumCollection.onCreate(this, this) + mAlbumCollection.loadAlbums() + } + + override fun onAlbumLoad(cursor: Cursor?) { + if (mIsFirstAlbumLoad) { + mIsFirstAlbumLoad = false + mAlbumsAdapter.swapCursor(cursor) + mBaseHandler.post { + cursor?.moveToPosition(mAlbumCollection.currentSelection) + val album = Album.valueOf(cursor) + if (album.isAll && SelectionSpec.getInstance().capture) { + album.addCaptureCount() + } + if (mLocalMediaFragment?.isAdded == true) { + mLocalMediaFragment?.loadVideos(album) + } + } + } + + } + + override fun onAlbumReset() { + + } + + + private fun setPhotoNavigationTitle(up: Boolean) { + val drawable = ContextCompat.getDrawable(HaloApp.getInstance().application, if (up) R.drawable.ic_video_arrow_up else R.drawable.ic_video_arrow_down) + val arrowIv = findViewById(R.id.arrowIv) + arrowIv?.setImageDrawable(drawable) + } + + companion object { + fun getIntent(context: Context, chooseType: ChooseType, maxChooseCount: Int = 1): Intent { + return Intent(context, LocalMediaActivity::class.java).apply { + putExtra(EntranceUtils.KEY_TYPE, chooseType.value) + putExtra(EntranceUtils.KEY_CHOOSE_MAX_COUNT, maxChooseCount) + } + } + } + + enum class ChooseType(val value: String) { + VIDEO("video"), + IMAGE("image") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaAdapter.kt new file mode 100644 index 0000000000..38d518f92d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaAdapter.kt @@ -0,0 +1,79 @@ +package com.gh.gamecenter.qa.editor + +import android.content.Context +import android.database.Cursor +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.LocalVideoItemBinding +import com.zhihu.matisse.internal.entity.Item +import com.zhihu.matisse.internal.ui.adapter.RecyclerViewCursorAdapter +import com.zhihu.matisse.internal.utils.PathUtils + +class LocalMediaAdapter(val context: Context, val mChooseType: String, val maxChooseSize: Int, val callback: (ArrayList) -> Unit) : RecyclerViewCursorAdapter(null) { + private val mSelectedMediaList = arrayListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalVideoPreviewViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.local_video_item, parent, false) + return LocalVideoPreviewViewHolder(LocalVideoItemBinding.bind(view)) + } + + override fun onBindViewHolder(holder: LocalVideoPreviewViewHolder, cursor: Cursor?, position: Int) { + val item = Item.valueOf(cursor) + holder.binding.durationTv.goneIf(mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) + val path = "file:///${PathUtils.getPath(context, item.contentUri)}" + ImageUtils.displayResizeMedia(holder.binding.preview, path, 200, 200) + holder.binding.durationTv.text = TimeUtils.formatVideoDuration(item.duration / 1000) + val drawable = if (mSelectedMediaList.contains(item)) { + if (maxChooseSize == 1) { + R.drawable.ic_choose_media_selected.toDrawable() + } else { + R.drawable.ic_choose_media_bg.toDrawable() + } + } else { + R.drawable.ic_choose_media_normal.toDrawable() + } + holder.binding.checkImageView.setImageDrawable(drawable) + if (mSelectedMediaList.contains(item) && maxChooseSize > 1) { + holder.binding.chooseCountTv.visibility = View.VISIBLE + holder.binding.chooseCountTv.text = (mSelectedMediaList.indexOf(item) + 1).toString() + } else { + holder.binding.chooseCountTv.visibility = View.GONE + } + holder.itemView.setOnClickListener { + if (mSelectedMediaList.contains(item)) { + mSelectedMediaList.remove(item) + notifyDataSetChanged() + callback.invoke(mSelectedMediaList) + } else { + if (maxChooseSize == 1) { + mSelectedMediaList.clear() + } + if (mSelectedMediaList.size < maxChooseSize) { + mSelectedMediaList.add(item) + notifyDataSetChanged() + callback.invoke(mSelectedMediaList) + } else { + if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) { + ToastUtils.showToast("至多选择${maxChooseSize}张图片") + } else { + ToastUtils.showToast("至多选择${maxChooseSize}条视频") + } + } + } + } + } + + override fun getItemViewType(position: Int, cursor: Cursor?): Int { + return 0 + } + + fun getSelectedMediaList(): ArrayList { + return mSelectedMediaList + } +} + +class LocalVideoPreviewViewHolder(val binding: LocalVideoItemBinding) : BaseRecyclerViewHolder(binding.root) diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaFragment.kt new file mode 100644 index 0000000000..aa1cb7e2d9 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalMediaFragment.kt @@ -0,0 +1,136 @@ +package com.gh.gamecenter.qa.editor + +import android.app.Activity +import android.content.Intent +import android.database.Cursor +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import androidx.recyclerview.widget.GridLayoutManager +import com.gh.base.fragment.BaseFragment +import com.gh.common.util.* +import com.gh.common.view.GridSpacingItemDecoration +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentLocalMediaBinding +import com.gh.gamecenter.entity.LocalVideoEntity +import com.zhihu.matisse.internal.entity.Album +import com.zhihu.matisse.internal.model.AlbumMediaCollection +import com.zhihu.matisse.internal.utils.PathUtils +import com.zhihu.matisse.ui.MatisseActivity + +class LocalMediaFragment : BaseFragment(), AlbumMediaCollection.AlbumMediaCallbacks { + private lateinit var mBinding: FragmentLocalMediaBinding + private lateinit var mAdapter: LocalMediaAdapter + private var mAlbumMediaCollection: AlbumMediaCollection? = null + private var mChooseType = "" + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout(): View { + mBinding = FragmentLocalMediaBinding.inflate(LayoutInflater.from(requireContext()), null, false) + return mBinding.root + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mChooseType = arguments?.getString(EntranceUtils.KEY_TYPE) ?: "" + mBinding.previewTv.goneIf(mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) + mBinding.reuseTvNoneData.text = "暂无数据~" + mBinding.listRv.layoutManager = GridLayoutManager(requireContext(), 3) + mBinding.listRv.addItemDecoration(GridSpacingItemDecoration(3, 4F.dip2px(), false)) + val maxChooseCount = arguments?.getInt(EntranceUtils.KEY_CHOOSE_MAX_COUNT, 1) + mAdapter = LocalMediaAdapter(requireContext(), mChooseType, maxChooseCount ?: 1) { + mBinding.previewTv.isEnabled = it.isNotEmpty() + mBinding.confirmTv.isEnabled = it.isNotEmpty() + if (it.isEmpty()) { + mBinding.previewTv.setTextColor(R.color.text_cccccc.toColor()) + mBinding.confirmTv.alpha = 0.6f + } else { + mBinding.previewTv.setTextColor(R.color.text_666666.toColor()) + mBinding.confirmTv.alpha = 1f + } + mBinding.numTv.text = "(${it.size}/${mAdapter.maxChooseSize})" + } + mBinding.numTv.text = "(0/${mAdapter.maxChooseSize})" + mBinding.listRv.adapter = mAdapter + mBinding.listRefresh.isEnabled = false + + mBinding.previewTv.setOnClickListener { + val intent = PreviewVideoActivity.getIntent(requireContext(), mAdapter.getSelectedMediaList()) + requireActivity().startActivityForResult(intent, PREVIEW_VIDEO) + } + mBinding.confirmTv.setOnClickListener { + val intent = Intent() + if (mChooseType == LocalMediaActivity.ChooseType.VIDEO.value) { + val localVideoList = arrayListOf() + mAdapter.getSelectedMediaList().forEach { + val path = PathUtils.getPath(requireContext(), it.contentUri) + val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis() + val format = getFileFormat(it.mimeType) + localVideoList.add(LocalVideoEntity(id, path, contentUri = it.contentUri, duration = it.duration, format = format, size = it.size)) + } + intent.putExtra(LocalVideoEntity::class.java.name, localVideoList) + } else { + val data = mAdapter.getSelectedMediaList().map { it.contentUri }.toList() + intent.putParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION, ArrayList(data)) + } + requireActivity().setResult(Activity.RESULT_OK, intent) + requireActivity().finish() + } + } + + private fun getFileFormat(mimeType: String?): String { + var format = "" + tryWithDefaultCatch { + if (mimeType != null) { + val split = mimeType.split("/") + format = if (split.count() >= 2) { + split[1] + } else { + mimeType + } + } + } + return format + } + + override fun onAlbumMediaReset() { + mAdapter.swapCursor(null) + } + + override fun onAlbumMediaLoad(cursor: Cursor?) { + mAdapter.swapCursor(cursor) + mBinding.reuseNoneData.visibility = View.GONE + mBinding.reuseLlLoading.root.visibility = View.GONE + mBinding.reuseNoConnection.root.visibility = View.GONE + mBinding.listRefresh.isRefreshing = false + } + + fun loadVideos(album: Album) { + mAlbumMediaCollection?.onDestroy() + + mAlbumMediaCollection = AlbumMediaCollection() + mAlbumMediaCollection?.onCreate(requireActivity(), this@LocalMediaFragment) + + mAlbumMediaCollection?.load(album) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (data != null) { + requireActivity().setResult(Activity.RESULT_OK, data) + requireActivity().finish() + } + } + + override fun onDestroy() { + super.onDestroy() + mAlbumMediaCollection?.onDestroy() + } + + companion object { + const val PREVIEW_VIDEO = 100 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/LocalVideoAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalVideoAdapter.kt deleted file mode 100644 index 146aeff7fa..0000000000 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/LocalVideoAdapter.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.gh.gamecenter.qa.editor - -import android.database.Cursor -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity -import com.gh.base.BaseRecyclerViewHolder -import com.gh.common.util.TimeUtils -import com.gh.gamecenter.R -import com.gh.gamecenter.databinding.LocalVideoItemBinding -import com.gh.gamecenter.databinding.PhotoPosterItemBinding -import com.gh.gamecenter.video.upload.view.UploadVideoActivity -import com.gh.gamecenter.video.videomanager.VideoManagerActivity -import com.halo.assistant.HaloApp -import com.zhihu.matisse.internal.entity.Item -import com.zhihu.matisse.internal.entity.SelectionSpec -import com.zhihu.matisse.internal.ui.adapter.RecyclerViewCursorAdapter -import com.zhihu.matisse.internal.utils.PathUtils - -class LocalVideoAdapter : RecyclerViewCursorAdapter(null) { - private val mImageResize = HaloApp.getInstance().application.resources.displayMetrics.widthPixels / 3 - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalVideoPreviewViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.local_video_item, parent, false) - return LocalVideoPreviewViewHolder(LocalVideoItemBinding.bind(view)) - } - - override fun onBindViewHolder(holder: LocalVideoPreviewViewHolder, cursor: Cursor?, position: Int) { - - val item = Item.valueOf(cursor) - SelectionSpec.getInstance().imageEngine.loadThumbnail( - HaloApp.getInstance().application, mImageResize, null, - holder.binding.preview, item.contentUri) - holder.binding.durationTv.text = TimeUtils.formatVideoDuration(item.duration / 1000) - holder.itemView.setOnClickListener { - val path = PathUtils.getPath(holder.itemView.context, item.contentUri) - val intent = UploadVideoActivity.getIntent(holder.itemView.context, path, "本地视频", "", "") - (holder.itemView.context as AppCompatActivity).startActivityForResult(intent, VideoActivity.INSERT_VIDEO_CODE) - } - } - - override fun getItemViewType(position: Int, cursor: Cursor?): Int { - return 0 - } -} - -class LocalVideoPreviewViewHolder(val binding: LocalVideoItemBinding) : BaseRecyclerViewHolder(binding.root) diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/LocalVideoFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/LocalVideoFragment.kt deleted file mode 100644 index abefa8c9f3..0000000000 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/LocalVideoFragment.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.gh.gamecenter.qa.editor - -import android.app.Activity -import android.content.Intent -import android.database.Cursor -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import androidx.recyclerview.widget.GridLayoutManager -import com.gh.base.fragment.BaseFragment -import com.gh.common.util.dip2px -import com.gh.common.view.GridSpacingItemDecoration -import com.gh.gamecenter.databinding.ActivityEditorVideoBinding -import com.gh.gamecenter.video.upload.view.UploadVideoActivity -import com.zhihu.matisse.internal.entity.Album -import com.zhihu.matisse.internal.model.AlbumMediaCollection - -class LocalVideoFragment : BaseFragment(), AlbumMediaCollection.AlbumMediaCallbacks { - lateinit var binding: ActivityEditorVideoBinding - private lateinit var mAdapter: LocalVideoAdapter - private var mAlbumMediaCollection: AlbumMediaCollection? = null - - override fun getLayoutId(): Int = 0 - - override fun getInflatedLayout(): View { - binding = ActivityEditorVideoBinding.inflate(LayoutInflater.from(requireContext()), null, false) - return binding.root - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding.reuseTvNoneData.text = "暂无数据~" - binding.listRv.layoutManager = GridLayoutManager(requireContext(), 3) - binding.listRv.addItemDecoration(GridSpacingItemDecoration(3, 1F.dip2px(), false)) - mAdapter = LocalVideoAdapter() - binding.listRv.adapter = mAdapter - binding.listRefresh.isEnabled = false - } - - override fun onAlbumMediaReset() { - mAdapter.swapCursor(null) - } - - override fun onAlbumMediaLoad(cursor: Cursor?) { - mAdapter.swapCursor(cursor) - binding.reuseNoneData.visibility = View.GONE - binding.reuseLlLoading.root.visibility = View.GONE - binding.reuseNoConnection.root.visibility = View.GONE - binding.listRefresh.isRefreshing = false - } - - fun loadVideos(album: Album) { - mAlbumMediaCollection?.onDestroy() - - mAlbumMediaCollection = AlbumMediaCollection() - mAlbumMediaCollection?.onCreate(requireActivity(), this@LocalVideoFragment) - - mAlbumMediaCollection?.load(album) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (data != null && requestCode == VideoActivity.INSERT_VIDEO_CODE) { - if (resultCode == UploadVideoActivity.RESULT_CODE_VIDEO) { - requireActivity().setResult(Activity.RESULT_OK, data) - requireActivity().finish() - } - } - } - - override fun onDestroy() { - super.onDestroy() - mAlbumMediaCollection?.onDestroy() - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt index 1ec96a8f27..52b235bb3d 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/OnLinkClickListener.kt @@ -79,4 +79,9 @@ class OnLinkClickListener(val context: Context, } } } + + @JavascriptInterface + fun onVideoClick(url: String, poster: String) { + FullScreenVideoActivity.start(context, url, poster) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoFragment.kt index 99cf758100..8f944f01ad 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoFragment.kt @@ -2,43 +2,29 @@ package com.gh.gamecenter.qa.editor import android.content.Intent import android.os.Bundle -import android.text.SpannableStringBuilder -import android.text.Spanned -import android.text.TextPaint -import android.text.method.LinkMovementMethod -import android.text.style.ClickableSpan import android.view.LayoutInflater import android.view.View -import android.widget.TextView -import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.gh.common.constant.Config import com.gh.common.util.* import com.gh.common.view.GridSpacingItemDecoration -import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment -import com.gh.gamecenter.baselist.NormalListViewModel -import com.gh.gamecenter.databinding.ActivityEditorVideoBinding +import com.gh.gamecenter.databinding.FragmentLocalMediaBinding import com.gh.gamecenter.entity.MyVideoEntity -import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.video.upload.view.UploadVideoActivity import com.gh.gamecenter.video.videomanager.VideoManagerActivity -import com.halo.assistant.HaloApp import com.zhihu.matisse.Matisse -import com.zhihu.matisse.MimeType import com.zhihu.matisse.internal.utils.PathUtils -import io.reactivex.Observable +@Deprecated("v5.0.0废弃") class OnlineVideoFragment : ListFragment() { - private lateinit var binding: ActivityEditorVideoBinding + private lateinit var binding: FragmentLocalMediaBinding private var mAdapter: VideoAdapter? = null override fun getInflatedLayout(): View { - binding = ActivityEditorVideoBinding.inflate(LayoutInflater.from(requireContext()), null, false) + binding = FragmentLocalMediaBinding.inflate(LayoutInflater.from(requireContext()), null, false) return binding.root } diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoViewModel.kt index 361bef900f..45156a4942 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/OnlineVideoViewModel.kt @@ -10,6 +10,7 @@ import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.retrofit.RetrofitManager import io.reactivex.Observable +@Deprecated("v5.0.0废弃") class OnlineVideoViewModel(application: Application) : ListViewModel(application) { override fun provideDataObservable(page: Int): Observable> { return RetrofitManager.getInstance(getApplication()).api.getCommunityInsertVideo(UserManager.getInstance().userId, page, Config.VIDEO_PAGE_SIZE) diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoActivity.kt new file mode 100644 index 0000000000..45421caa37 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoActivity.kt @@ -0,0 +1,34 @@ +package com.gh.gamecenter.qa.editor + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.base.BaseActivity +import com.gh.common.util.DisplayUtils +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.R +import com.zhihu.matisse.internal.entity.Item + +class PreviewVideoActivity : BaseActivity() { + override fun getLayoutId(): Int { + return R.layout.activity_amway + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + DisplayUtils.transparentStatusBar(this) + + val containerFragment = supportFragmentManager.findFragmentByTag(PreviewVideoFragment::class.java.simpleName) + ?: PreviewVideoFragment().with(intent.extras) + // 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移 + supportFragmentManager.beginTransaction().replace(R.id.placeholder, containerFragment, PreviewVideoFragment::class.java.simpleName).commitAllowingStateLoss() + } + + companion object { + fun getIntent(context: Context,videos:ArrayList): Intent { + val intent = Intent(context, PreviewVideoActivity::class.java) + intent.putExtra(EntranceUtils.KEY_VIDEO_LIST,videos) + return intent + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoFragment.kt new file mode 100644 index 0000000000..e6e0959d3a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoFragment.kt @@ -0,0 +1,202 @@ +package com.gh.gamecenter.qa.editor + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder +import com.facebook.drawee.generic.RoundingParams +import com.facebook.drawee.view.SimpleDraweeView +import com.gh.base.fragment.BaseFragment +import com.gh.base.fragment.WaitingDialogFragment +import com.gh.common.util.* +import com.gh.common.view.GridSpacingItemColorDecoration +import com.gh.gamecenter.CropImageActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentPreviewVideoBinding +import com.gh.gamecenter.databinding.ItemVideoSelectorBinding +import com.gh.gamecenter.entity.LocalVideoEntity +import com.gh.gamecenter.video.poster.PosterEditActivity +import com.gh.gamecenter.video.upload.view.UploadVideoActivity +import com.lightgame.adapter.BaseRecyclerAdapter +import com.lightgame.download.FileUtils +import com.shuyu.gsyvideoplayer.GSYVideoManager +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.zhihu.matisse.internal.entity.Item +import com.zhihu.matisse.internal.utils.PathUtils + +class PreviewVideoFragment : BaseFragment() { + private lateinit var mBinding: FragmentPreviewVideoBinding + private var mVideoItems: ArrayList = arrayListOf() + private var mProcessingDialog: WaitingDialogFragment? = null + private lateinit var mVideoSelectorAdapter: VideoSelectorAdapter + private val mLocalVideoList = arrayListOf() + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout(): View { + mBinding = FragmentPreviewVideoBinding.inflate(LayoutInflater.from(requireContext()), null, false) + return mBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mVideoItems = arguments?.getParcelableArrayList(EntranceUtils.KEY_VIDEO_LIST) + ?: arrayListOf() + if (mVideoItems.isNotEmpty()) { + mVideoItems.forEach { + val path = PathUtils.getPath(requireContext(), it.contentUri) + val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis() + val format = getFileFormat(it.mimeType) + val localVideoEntity = LocalVideoEntity( + id = id, + filePath = path, + contentUri = it.contentUri, + duration = it.duration, + format = format, + size = it.size + ) + mLocalVideoList.add(localVideoEntity) + } + initVideo(mLocalVideoList[0]) + } + mBinding.numTv.text = "(1/${mVideoItems.size})" + mVideoSelectorAdapter = VideoSelectorAdapter(requireContext(), mLocalVideoList) { entity, position -> + mBinding.videoView.release() + mBinding.numTv.text = "(${position + 1}/${mVideoItems.size})" + initVideo(entity) + } + mBinding.videoSelectorRv.adapter = mVideoSelectorAdapter + mBinding.videoSelectorRv.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) + mBinding.videoSelectorRv.addItemDecoration(GridSpacingItemColorDecoration(requireContext(), 8, 0, R.color.transparent)) + mBinding.confirmTv.setOnClickListener { + val intent = Intent() + intent.putExtra(LocalVideoEntity::class.java.name, mLocalVideoList) + requireActivity().setResult(Activity.RESULT_OK, intent) + requireActivity().finish() + } + + mBinding.changeCoverTv.setOnClickListener { + val item = mVideoItems[mVideoSelectorAdapter.selectPosition] + val intent = PosterEditActivity.getIntentByPath(requireContext(), PathUtils.getPath(requireContext(), item.uri)) + startActivityForResult(intent, UploadVideoActivity.REQUEST_CODE_IMAGE_CROP) + } + mBinding.backBtn.setOnClickListener { requireActivity().finish() } + } + + private fun getFileFormat(mimeType: String?): String { + var format = "" + tryWithDefaultCatch { + if (mimeType != null) { + val split = mimeType.split("/") + format = if (split.count() >= 2) { + split[1] + } else { + mimeType + } + } + } + return format + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (data == null || resultCode != Activity.RESULT_OK) return + if (requestCode == UploadVideoActivity.REQUEST_CODE_IMAGE_CROP) { + val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: "" + if (imagePath.isNotEmpty()) { + uploadImage(imagePath) + } + } + } + + private fun uploadImage(imagePath: String) { + mProcessingDialog = WaitingDialogFragment.newInstance("图片上传中...", false) + mProcessingDialog?.show(requireActivity().supportFragmentManager, WaitingDialogFragment::class.java.name) + UploadImageUtils.uploadImage(UploadImageUtils.UploadType.poster, imagePath, object : UploadImageUtils.OnUploadImageListener { + override fun onSuccess(imageUrl: String) { + mProcessingDialog?.dismiss() + mLocalVideoList[mVideoSelectorAdapter.selectPosition].poster = imageUrl + mBinding.videoView.updateThumb(imageUrl) + } + + override fun onError(e: Throwable?) { + mProcessingDialog?.dismiss() + ToastUtils.showToast("上传失败") + } + + override fun onProgress(total: Long, progress: Long) {} + }) + } + + private fun initVideo(entity: LocalVideoEntity) { + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(entity.filePath) + .setRotateViewAuto(false) + .setCacheWithPlay(false) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .build(mBinding.videoView) + if (entity.poster.isNotEmpty()) { + mBinding.videoView.updateThumb(entity.poster) + } else { + mBinding.videoView.updateThumb("file:///${PathUtils.getPath(requireContext(), entity.contentUri)}") + } + } + + class VideoSelectorAdapter(context: Context, val localVideoList: ArrayList, val callback: (LocalVideoEntity, Int) -> Unit) : BaseRecyclerAdapter(context) { + var selectPosition: Int = 0 + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return VideoSelectorViewHolder(ItemVideoSelectorBinding.inflate(LayoutInflater.from(mContext), parent, false)) + } + + override fun getItemCount(): Int = localVideoList.size + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is VideoSelectorViewHolder) { + val item = localVideoList[position] + setPreviewBorder(holder.binding.preview, selectPosition == position) + ImageUtils.display(holder.binding.preview, "file:///${PathUtils.getPath(mContext, item.contentUri)}") + holder.binding.root.setOnClickListener { + selectPosition = position + callback.invoke(localVideoList[selectPosition], selectPosition) + notifyDataSetChanged() + } + } + } + + private fun setPreviewBorder(view: SimpleDraweeView, isSelected: Boolean) { + val params = RoundingParams() + params.setBorder(if (isSelected) R.color.white.toColor() else R.color.transparent.toColor(), 1f.dip2px().toFloat()) + params.setCornersRadius(4f.dip2px().toFloat()) + val build = GenericDraweeHierarchyBuilder.newInstance(mContext.resources) + .setRoundingParams(params) + .build() + view.hierarchy = build + } + } + + class VideoSelectorViewHolder(val binding: ItemVideoSelectorBinding) : RecyclerView.ViewHolder(binding.root) + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onDestroy() { + super.onDestroy() + GSYVideoManager.releaseAllVideos() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoView.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoView.kt new file mode 100644 index 0000000000..5bf4ec6366 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/PreviewVideoView.kt @@ -0,0 +1,85 @@ +package com.gh.gamecenter.qa.editor + +import android.content.Context +import android.util.AttributeSet +import android.view.GestureDetector +import android.view.GestureDetector.SimpleOnGestureListener +import android.view.MotionEvent +import android.view.Surface +import android.view.View +import android.widget.ImageView +import com.facebook.drawee.view.SimpleDraweeView +import com.gh.common.util.ImageUtils +import com.gh.gamecenter.R +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView + +class PreviewVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : StandardGSYVideoPlayer(context, attrs) { + + var thumbImage: SimpleDraweeView = findViewById(R.id.thumbImage) + + override fun getLayoutId(): Int { + return R.layout.layout_preview_video + } + + override fun updateStartImage() { + if (mStartButton is ImageView) { + val imageView = mStartButton as ImageView + when (mCurrentState) { + GSYVideoView.CURRENT_STATE_PLAYING -> imageView.setImageResource(R.drawable.icon_preview_video_pause) + GSYVideoView.CURRENT_STATE_ERROR -> imageView.setImageResource(R.drawable.icon_preview_video_play) + else -> imageView.setImageResource(R.drawable.icon_preview_video_play) + } + } + } + + override fun onClickUiToggle() { + if (mCurrentState == CURRENT_STATE_PLAYING) { + if (mStartButton.visibility == View.VISIBLE) { + changeUiToPlayingClear() + } else { + changeUiToPlayingShow() + } + } else { + super.onClickUiToggle() + } + } + + /******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/ + + override fun onSurfaceUpdated(surface: Surface) { + super.onSurfaceUpdated(surface) + if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) { + mThumbImageViewLayout.visibility = View.INVISIBLE + } + } + + fun updateThumb(url: String) { + ImageUtils.display(thumbImage, url) + } + + override fun setViewShowState(view: View?, visibility: Int) { + if (view === mThumbImageViewLayout && visibility != View.VISIBLE) { + return + } + super.setViewShowState(view, visibility) + } + + /********************************各类UI的状态显示*********************************************/ + + override fun touchSurfaceMoveFullLogic(absDeltaX: Float, absDeltaY: Float) { + super.touchSurfaceMoveFullLogic(absDeltaX, absDeltaY) + //不给触摸快进,如果需要,屏蔽下方代码即可 + mChangePosition = false + //不给触摸音量,如果需要,屏蔽下方代码即可 + mChangeVolume = false + //不给触摸亮度,如果需要,屏蔽下方代码即可 + mBrightness = false + } + + override fun isShowNetConfirm(): Boolean = false + + override fun touchDoubleUp() { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/VideoActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/VideoActivity.kt deleted file mode 100644 index d4034d8471..0000000000 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/VideoActivity.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.gh.gamecenter.qa.editor - -import android.annotation.SuppressLint -import android.content.Context -import android.content.Intent -import android.database.Cursor -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.widget.AdapterView -import android.widget.ImageView -import android.widget.PopupWindow -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.viewpager.widget.ViewPager -import com.gh.base.ToolBarActivity -import com.gh.base.adapter.FragmentAdapter -import com.gh.base.fragment.BaseFragment_TabLayout -import com.gh.common.util.viewModelProvider -import com.gh.common.view.TabIndicatorView -import com.gh.gamecenter.R -import com.gh.gamecenter.baselist.LoadType -import com.gh.gamecenter.databinding.TabItemChooseVideoBinding -import com.gh.gamecenter.video.poster.photo.PhotoAlbumsAdapter -import com.gh.gamecenter.video.poster.photo.PhotoAlbumsSpanner -import com.google.android.material.tabs.TabLayout -import com.halo.assistant.HaloApp -import com.zhihu.matisse.Matisse -import com.zhihu.matisse.MimeType -import com.zhihu.matisse.internal.entity.Album -import com.zhihu.matisse.internal.entity.SelectionSpec -import com.zhihu.matisse.internal.model.AlbumCollection -import kotterknife.bindView - - -/** - * 插入视频页面 - */ -class VideoActivity : ToolBarActivity(), AlbumCollection.AlbumCallbacks { - private val mTabIndicatorView by bindView(R.id.activity_tab_indicator) - private val mTabLayout by bindView(R.id.activity_tab_layout) - private val mViewPager by bindView(R.id.activity_view_pager) - private lateinit var mViewModel: OnlineVideoViewModel - private var mFragmentsList: ArrayList = arrayListOf() - private var mTabTitleList: ArrayList = arrayListOf() - private var mLocalVideoFragment: LocalVideoFragment? = null - private lateinit var mAlbumsSpinner: VideoAlbumsSpanner - private lateinit var mAlbumsAdapter: VideoAlbumsAdapter - private val mAlbumCollection = AlbumCollection() - - override fun getLayoutId(): Int = R.layout.activity_video_tablayout_viewpager - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - mViewModel = viewModelProvider() - mViewModel.obsListData.observe(this, Observer { - initViewPager() - initAlbumsSpinner() - }) - mViewModel.load(LoadType.REFRESH) - } - - private fun initAlbumsSpinner() { - mAlbumsSpinner = VideoAlbumsSpanner(this) - mAlbumsAdapter = VideoAlbumsAdapter(this) - - mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.normal_toolbar)) - mAlbumsSpinner.setAdapter(mAlbumsAdapter) - mAlbumsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) { - - } - - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - mAlbumCollection.setStateCurrentSelection(position) - mAlbumsAdapter.cursor.moveToPosition(position) - val album = Album.valueOf(mAlbumsAdapter.cursor) - if (album.isAll && SelectionSpec.getInstance().capture) { - album.addCaptureCount() - } - if (mLocalVideoFragment?.isAdded == true) { - mLocalVideoFragment?.loadVideos(album) - } - } - } - - mAlbumsSpinner.setDismissListener(PopupWindow.OnDismissListener { - setPhotoNavigationTitle(false) - }) - //必须加这行代码,[SelectionSpec]是单例模式,下次使用必须先更新MimeType - Matisse.from(this).choose(MimeType.ofVideo()).showSingleMediaType(true) - mAlbumCollection.onCreate(this, this) - mAlbumCollection.loadAlbums() - } - - - override fun onAlbumLoad(cursor: Cursor?) { - mAlbumsAdapter.swapCursor(cursor) - mBaseHandler.post { - cursor?.moveToPosition(mAlbumCollection.currentSelection) - val album = Album.valueOf(cursor) - if (album.isAll && SelectionSpec.getInstance().capture) { - album.addCaptureCount() - } - if (mLocalVideoFragment?.isAdded == true) { - mLocalVideoFragment?.loadVideos(album) - } - } - } - - override fun onAlbumReset() { - - } - - private fun initViewPager() { - if (!mViewModel.obsListData.value.isNullOrEmpty()) { - mTabTitleList.add("视频库") - mFragmentsList.add(OnlineVideoFragment()) - } else { - mTabIndicatorView.visibility = View.GONE - } - mTabTitleList.add("本地视频") - mLocalVideoFragment = LocalVideoFragment() - mFragmentsList.add(mLocalVideoFragment!!) - mViewPager.offscreenPageLimit = mFragmentsList.size - mViewPager.adapter = FragmentAdapter(supportFragmentManager, mFragmentsList, mTabTitleList) - mViewPager.currentItem = 0 - mTabLayout.setupWithViewPager(mViewPager) - mTabIndicatorView.setupWithTabLayout(mTabLayout) - mTabIndicatorView.setupWithViewPager(mViewPager) - mTabIndicatorView.setIndicatorWidth(18) - - for (i in 0 until mTabLayout.tabCount) { - val tab = mTabLayout.getTabAt(i) ?: continue - val tabTitle = if (tab.text != null) tab.text.toString() else "" - val tabView: View = provideTabView(tabTitle) - tab.customView = tabView - } - - BaseFragment_TabLayout.initTabStyle(mTabLayout, 0) - } - - @SuppressLint("ClickableViewAccessibility") - private fun provideTabView(tabTitle: String): View { - val binding = TabItemChooseVideoBinding.inflate(LayoutInflater.from(this), null, false) - binding.tabTitle.text = tabTitle - if (mViewModel.obsListData.value.isNullOrEmpty()) { - binding.tabTitle.setTextColor(ContextCompat.getColor(this, R.color.text_333333)) - } - if (tabTitle == "本地视频") { - binding.tabArrow.visibility = View.VISIBLE - binding.root.setOnTouchListener { _, _ -> - if (mViewPager.currentItem == mFragmentsList.size - 1) { - mAlbumsSpinner.show(mViewPager.height) - setPhotoNavigationTitle(true) - true - } else false - } - } - return binding.root - } - - private fun setPhotoNavigationTitle(up: Boolean) { - val drawable = ContextCompat.getDrawable(HaloApp.getInstance().application, if (up) R.drawable.ic_video_arrow_up else R.drawable.ic_video_arrow_down) - val tabArrow = mTabLayout.getTabAt(mFragmentsList.size - 1)?.customView?.findViewById(R.id.tab_arrow) - tabArrow?.setImageDrawable(drawable) - } - - companion object { - const val INSERT_VIDEO_CODE = 414 - - fun getIntent(context: Context): Intent { - return Intent(context, VideoActivity::class.java) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAdapter.kt index 3a58f7a1d4..e480dedb69 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAdapter.kt @@ -22,7 +22,7 @@ import com.gh.gamecenter.video.VideoVerifyItemViewHolder import java.text.SimpleDateFormat import java.util.* - +@Deprecated("v5.0.0废弃") class VideoAdapter(context: Context, val mViewModel: OnlineVideoViewModel) : ListAdapter(context) { diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/AnswerEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/AnswerEntity.kt index 09361c5e1a..7c3fa19d73 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/AnswerEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/AnswerEntity.kt @@ -9,10 +9,10 @@ import androidx.room.PrimaryKey import androidx.room.TypeConverters import com.gh.common.annotation.SyncIgnore import com.gh.common.annotation.SyncPage +import com.gh.common.constant.Constants import com.gh.common.syncpage.SyncFieldConstants -import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.entity.MeEntity -import com.gh.gamecenter.entity.UserEntity +import com.gh.common.util.SPUtils +import com.gh.gamecenter.entity.* import com.gh.gamecenter.room.converter.* import com.google.gson.annotations.SerializedName @@ -40,7 +40,6 @@ class AnswerEntity() : Parcelable { @SerializedName("sequence_id") var sequenceId: String? = null - @SerializedName("brief", alternate = ["content"]) var brief: String? = null @SerializedName("title") @@ -113,6 +112,21 @@ class AnswerEntity() : Parcelable { @SerializedName("popular_answer") var popularAnswer: AnswerEntity? = null + var des: String = "" + + var url: String = "" + + @TypeConverters(VideoInfoConverter::class) + @SerializedName("video_info") + var videoInfo: VideoInfo = VideoInfo() + + var poster: String = "" + + var length: Long = 0 + + @Ignore + var count: Count = Count() + fun getPassVideos(): List { val passVideos = arrayListOf() for (video in videos) { @@ -138,6 +152,12 @@ class AnswerEntity() : Parcelable { active = parcel.readByte() != 0.toByte() type = parcel.readString() ?: "" time = parcel.readLong() + des = parcel.readString() ?: "" + url = parcel.readString() ?: "" + videoInfo = parcel.readParcelable(VideoInfo::class.java.classLoader) ?: VideoInfo() + poster = parcel.readString() ?: "" + length = parcel.readLong() + count = parcel.readParcelable(Count::class.java.classLoader) ?: Count() } override fun writeToParcel(parcel: Parcel, flags: Int) { @@ -157,12 +177,71 @@ class AnswerEntity() : Parcelable { parcel.writeByte(if (active) 1 else 0) parcel.writeString(type) parcel.writeLong(time ?: 0) + parcel.writeString(des) + parcel.writeString(url) + parcel.writeParcelable(videoInfo, flags) + parcel.writeString(poster) + parcel.writeLong(length) + parcel.writeParcelable(count, flags) } override fun describeContents(): Int { return 0 } + fun transformForumVideoEntity(): ForumVideoEntity { + val forumVideoEntity = ForumVideoEntity() + if (type.contains("video")) { + forumVideoEntity.id = id ?: "" + forumVideoEntity.title = articleTitle ?: "" + forumVideoEntity.des = des + forumVideoEntity.url = url + forumVideoEntity.poster = poster + forumVideoEntity.length = length + forumVideoEntity.videoInfo = videoInfo + forumVideoEntity.videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + } else { + forumVideoEntity.title = questions.title ?: "" + forumVideoEntity.des = brief ?: "" + forumVideoEntity.videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + if (getPassVideos().isNotEmpty()) { + forumVideoEntity.id = getPassVideos()[0].id + forumVideoEntity.url = getPassVideos()[0].url + forumVideoEntity.poster = getPassVideos()[0].poster + forumVideoEntity.videoInfo = VideoInfo(width = getPassVideos()[0].width, height = getPassVideos()[0].height) + } + } + return forumVideoEntity + } + + fun transformArticleEntity(): ArticleEntity { + val articleEntity = ArticleEntity() + articleEntity.id = id ?: "" + articleEntity.brief = if (!brief.isNullOrEmpty()) brief ?: "" else description ?: "" + articleEntity.title = articleTitle ?: "" + articleEntity.images = images + articleEntity.videos = videos + articleEntity.user = user + articleEntity.time = TimeEntity(time ?: 0) + articleEntity.count = count + articleEntity.active = active + articleEntity.orderTag = orderTag + articleEntity.read = read + articleEntity.me = me + articleEntity.commentable = commentable + articleEntity.community = CommunityEntity(bbs.id, bbs.name, icon = bbs.game?.getIcon(), iconSubscript = bbs.game?.iconSubscript, game = bbs.game) + articleEntity.bbs = articleEntity.community + articleEntity.imagesInfo = imagesInfo + articleEntity.des = des + articleEntity.url = url + articleEntity.videoInfo = videoInfo + articleEntity.poster = poster + articleEntity.length = length + articleEntity.type = type + articleEntity.count = count + return articleEntity + } + companion object { const val TAG: String = "AnswerEntity" diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDetailEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDetailEntity.kt index e6c7531c98..dfb6413e85 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDetailEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDetailEntity.kt @@ -2,6 +2,7 @@ package com.gh.gamecenter.qa.entity import android.os.Parcelable import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.MeEntity import com.gh.gamecenter.entity.UserEntity import com.google.gson.annotations.SerializedName @@ -26,6 +27,13 @@ data class ArticleDetailEntity(@SerializedName("_id") var images: List = ArrayList(), @SerializedName("images_info") var imagesInfo: List = ArrayList(), - var videos: List = ArrayList()) : Parcelable + var videos: List = ArrayList(), + @SerializedName("tag_activity_id") + var tagActivityId: String = "", + @SerializedName("tag_activity_name") + var tagActivityName: String = "", + var type: String = "", + var gameEntity: GameEntity? = null +) : Parcelable diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDraftEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDraftEntity.kt index f53ee8f15e..47eaba566b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDraftEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleDraftEntity.kt @@ -2,6 +2,7 @@ package com.gh.gamecenter.qa.entity import android.os.Parcelable import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.GameEntity import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.Parcelize @@ -14,7 +15,14 @@ data class ArticleDraftEntity(@SerializedName("_id") val time: Time = Time(), @SerializedName("article_id") val articleId: String = "", - val community: CommunityEntity = CommunityEntity()) : Parcelable + val community: CommunityEntity = CommunityEntity(), + @SerializedName("tag_activity_id") + var tagActivityId: String = "", + @SerializedName("tag_activity_name") + var tagActivityName: String = "", + var type: String = "", + var gameEntity: GameEntity? = null +) : Parcelable @Parcelize data class Time(val create: Long = 0, diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt index 0f1c68b709..fa82bb486c 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/ArticleEntity.kt @@ -6,14 +6,11 @@ import androidx.room.Ignore import androidx.room.PrimaryKey import androidx.room.TypeConverters import com.gh.common.annotation.SyncPage +import com.gh.common.constant.Constants import com.gh.common.syncpage.SyncFieldConstants -import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.entity.MeEntity -import com.gh.gamecenter.entity.UserEntity -import com.gh.gamecenter.room.converter.CommunityVideoConverter -import com.gh.gamecenter.room.converter.ImageInfoConverter -import com.gh.gamecenter.room.converter.ListStringConverter -import com.gh.gamecenter.room.converter.StringArrayListConverter +import com.gh.common.util.SPUtils +import com.gh.gamecenter.entity.* +import com.gh.gamecenter.room.converter.* import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.Parcelize @@ -45,7 +42,16 @@ data class ArticleEntity( @Ignore var commentable: Boolean = true, @Ignore - var bbs: CommunityEntity = CommunityEntity() + var bbs: CommunityEntity = CommunityEntity(), + var des: String = "", + var url: String = "", + @TypeConverters(VideoInfoConverter::class) + @SerializedName("video_info") + var videoInfo: VideoInfo = VideoInfo(), + var poster: String = "", + var length: Long = 0, + @Ignore + var type: String = "" ) : Parcelable { fun getPassVideos(): List { @@ -69,6 +75,7 @@ data class ArticleEntity( answer.vote = count.vote answer.active = active answer.commentCount = count.comment + answer.answerCount = count.answer answer.orderTag = orderTag answer.read = read answer.me = me @@ -76,10 +83,41 @@ data class ArticleEntity( answer.communityId = community.id answer.communityName = community.name answer.bbs = bbs - answer.type = "community_article" + answer.type = type answer.imagesInfo = imagesInfo + answer.des = des + answer.url = url + answer.videoInfo = videoInfo + answer.poster = poster + answer.length = length + answer.count = count return answer } + + fun transformForumVideoEntity(): ForumVideoEntity { + val forumVideoEntity = ForumVideoEntity() + if (type.contains("video")) { + forumVideoEntity.id = id ?: "" + forumVideoEntity.title = title + forumVideoEntity.des = des + forumVideoEntity.url = url + forumVideoEntity.poster = poster + forumVideoEntity.length = length + forumVideoEntity.videoInfo = videoInfo + forumVideoEntity.videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + } else { + forumVideoEntity.title = title ?: "" + forumVideoEntity.des = brief ?: "" + if (getPassVideos().isNotEmpty()) { + forumVideoEntity.id = getPassVideos()[0].id + forumVideoEntity.url = getPassVideos()[0].url + forumVideoEntity.poster = getPassVideos()[0].poster + forumVideoEntity.videoInfo = VideoInfo(width = getPassVideos()[0].width, height = getPassVideos()[0].height) + forumVideoEntity.videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + } + } + return forumVideoEntity + } } @Parcelize @@ -89,7 +127,8 @@ data class Count( @SyncPage(syncNames = [SyncFieldConstants.ARTICLE_VOTE_COUNT, SyncFieldConstants.ANSWER_VOTE_COUNT]) var vote: Int = 0, @SyncPage(syncNames = [SyncFieldConstants.ANSWER_COUNT]) - var answer: Int = 0) : Parcelable + var answer: Int = 0, + var favorite: Int = 0) : Parcelable @Parcelize data class TimeEntity(var create: Long = 0, diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/CommunityVideoEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/CommunityVideoEntity.kt index 0f07eceb69..d8e1e40c23 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/CommunityVideoEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/CommunityVideoEntity.kt @@ -1,6 +1,7 @@ package com.gh.gamecenter.qa.entity import android.os.Parcelable +import kotlinx.android.parcel.IgnoredOnParcel import kotlinx.android.parcel.Parcelize @Parcelize @@ -10,4 +11,8 @@ data class CommunityVideoEntity(val id: String = "", var status: String = "", // 有三种状态 pass通过,fail未通过,pending审核中 val poster: String = "", var width: Int = 0, - var height: Int = 0) : Parcelable \ No newline at end of file + var height: Int = 0, + //本地数据 + @IgnoredOnParcel + var videoIsMuted: Boolean = false //是否静音标记 +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionDraftEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionDraftEntity.kt index d6c86844ad..d334c829f7 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionDraftEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionDraftEntity.kt @@ -2,6 +2,7 @@ package com.gh.gamecenter.qa.entity import android.os.Parcelable import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.GameEntity import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.Parcelize @@ -18,5 +19,13 @@ data class QuestionDraftEntity( var videos: ArrayList = arrayListOf(), var tags: List = arrayListOf(), @SerializedName("question_id") - var questionId: String = "" + var questionId: String = "", + @SerializedName("game_id") + var gameId: String = "", + @SerializedName("tag_activity_id") + var tagActivityId: String = "", + @SerializedName("tag_activity_name") + var tagActivityName: String = "", + var type: String = "", + var gameEntity: GameEntity? = null ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionsDetailEntity.kt b/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionsDetailEntity.kt index 9964c94d11..a42868709b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionsDetailEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/entity/QuestionsDetailEntity.kt @@ -2,6 +2,7 @@ package com.gh.gamecenter.qa.entity import android.os.Parcelable import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.MeEntity import com.gh.gamecenter.entity.UserEntity import com.google.gson.annotations.SerializedName @@ -12,27 +13,42 @@ import kotlinx.android.parcel.Parcelize */ @Parcelize -data class QuestionsDetailEntity(var id: String? = null, - var tags: List = ArrayList(), - var title: String? = null, - var description: String? = null, - @SerializedName("related_question") - var relatedQuestion: RelatedQuestion? = null, - var images: List = ArrayList(), - @SerializedName("answer_count") - var answersCount: Int = 0, - @SerializedName("answer_fold") - var isAnswerFold: Boolean = false, - var community: CommunityEntity = CommunityEntity(), - var isExistDrafts: Boolean = false, - var videos: List = ArrayList(), - var me: MeEntity = MeEntity(), - @SerializedName("follow_count") - private var followCount: Int = 0, - var user: UserEntity = UserEntity(), - //提交问题用 - @SerializedName("draft_id") - var draftId: String = "" +data class QuestionsDetailEntity( + @SerializedName("_id") + var id: String? = null, + var tags: List = ArrayList(), + var title: String? = null, + @SerializedName("description", alternate = ["content"]) + var description: String? = null, + @SerializedName("related_question") + var relatedQuestion: RelatedQuestion? = null, + var images: List = ArrayList(), + @Deprecated("废弃") + @SerializedName("answer_count") + var answersCount: Int = 0, + @SerializedName("answer_fold") + var isAnswerFold: Boolean = false, + @SerializedName("bbs") + var community: CommunityEntity = CommunityEntity(), + var isExistDrafts: Boolean = false, + var videos: List = ArrayList(), + var me: MeEntity = MeEntity(), + @SerializedName("follow_count") + private var followCount: Int = 0, + var user: UserEntity = UserEntity(), + @SerializedName("game_id") + var gameId: String = "", + @SerializedName("tag_activity_id") + var tagActivityId: String = "", + @SerializedName("tag_activity_name") + var tagActivityName: String = "", + var type: String = "", + var gameEntity: GameEntity? = null, + var time: TimeEntity = TimeEntity(), + var count: Count = Count(), + //提交问题用 + @SerializedName("draft_id") + var draftId: String = "" ) : Parcelable { fun getFollowCount(): Int { diff --git a/app/src/main/java/com/gh/gamecenter/qa/follow/AskFollowAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/follow/AskFollowAdapter.kt index 4e7d322761..554ecf0ba7 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/follow/AskFollowAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/follow/AskFollowAdapter.kt @@ -17,6 +17,7 @@ import com.gh.gamecenter.personalhome.home.UserHistoryAdapter import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity class AskFollowAdapter(val context: Context, val viewModel: AskFollowViewModel) : ListAdapter(context), ISyncAdapterHandler { @@ -108,7 +109,7 @@ class AskFollowAdapter(val context: Context, val viewModel: AskFollowViewModel) } else { historyEntity?.question?.id } - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, id, mEntrance, path)) + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, id?:"", mEntrance, path)) } } diff --git a/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAnswerAdapter.java b/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAnswerAdapter.java index 53ec5d94c0..2ef610313c 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAnswerAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAnswerAdapter.java @@ -17,6 +17,7 @@ import com.gh.gamecenter.qa.answer.MyAnswerViewHolder; import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity; import com.gh.gamecenter.qa.entity.AnswerEntity; import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity; +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity; import org.jetbrains.annotations.Nullable; @@ -67,13 +68,14 @@ class MyAnswerAdapter extends ListAdapter implements ISyncAdapterH answerViewHolder.bindItem(entity, mEntrance); CommunityMyAnswerItemBinding binding = answerViewHolder.getBinding(); binding.imageContainer.bindData(entity, mEntrance, ""); - binding.title.setOnClickListener(v -> - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, - entity.getQuestions().getId(), mEntrance, "我的光环-我的问答-我的回答"))); - - holder.itemView.setOnClickListener(v -> - mContext.startActivity(AnswerDetailActivity.getIntent(mContext, - entity.getId(), mEntrance, "我的光环-我的问答-我的回答"))); + binding.title.setOnClickListener(v -> { + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, + entity.getQuestions().getId(), mEntrance, "我的光环-我的问答-我的回答")); + }); + holder.itemView.setOnClickListener(v -> { + mContext.startActivity(NewQuestionDetailActivity.getCommentIntent(mContext, + entity.getQuestions().getId(), entity.getId(), mEntrance, "我的光环-我的问答-我的回答")); + }); break; case ItemViewType.ITEM_FOOTER: diff --git a/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAskWrapperFragment.java b/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAskWrapperFragment.java index 800b729c1e..eb99b2f3ed 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAskWrapperFragment.java +++ b/app/src/main/java/com/gh/gamecenter/qa/myqa/MyAskWrapperFragment.java @@ -27,16 +27,16 @@ public class MyAskWrapperFragment extends BaseFragment_TabLayout { String entrance = null; if (getArguments() != null) entrance = getArguments().getString(EntranceUtils.KEY_ENTRANCE); - fragments.add(ConcernQuestionsFragment.getInstance(entrance)); - fragments.add(MyQuestionsFragment.getInstance(entrance)); +// fragments.add(ConcernQuestionsFragment.getInstance(entrance)); +// fragments.add(MyQuestionsFragment.getInstance(entrance)); fragments.add(MyAnswerFragment.getInstance(entrance)); fragments.add(MyArticleFragment.getInstance(entrance, UserManager.getInstance().getUserId())); } @Override protected void initTabTitleList(List tabTitleList) { - tabTitleList.add("关注问题"); - tabTitleList.add("我的问题"); +// tabTitleList.add("关注问题"); +// tabTitleList.add("我的问题"); tabTitleList.add("我的回答"); tabTitleList.add("我的帖子"); } diff --git a/app/src/main/java/com/gh/gamecenter/qa/myqa/MyQuestionsAdapter.java b/app/src/main/java/com/gh/gamecenter/qa/myqa/MyQuestionsAdapter.java index 6784514e52..fa468007de 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/myqa/MyQuestionsAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/qa/myqa/MyQuestionsAdapter.java @@ -17,6 +17,7 @@ import com.gh.gamecenter.databinding.CommunityQuestionItemBinding; import com.gh.gamecenter.qa.entity.Questions; import com.gh.gamecenter.qa.questions.CommunityQuestionViewHolder; import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity; +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity; import kotlin.Pair; @@ -62,7 +63,7 @@ public class MyQuestionsAdapter extends ListAdapter implements ISyncA case ItemViewType.ITEM_BODY: Questions questions = mEntityList.get(position); ((CommunityQuestionViewHolder) holder).bindMyQuestionViewHolder(questions,mEntrance,"我的光环-我的问答-我的问题"); - holder.itemView.setOnClickListener(v -> mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.getId(), mEntrance, "我的光环-我的问答-我的问题"))); + holder.itemView.setOnClickListener(v -> mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, questions.getId(), mEntrance, "我的光环-我的问答-我的问题"))); break; case ItemViewType.ITEM_FOOTER: FooterViewHolder footerViewHolder = (FooterViewHolder) holder; diff --git a/app/src/main/java/com/gh/gamecenter/qa/newest/AskQuestionsNewBodyFragment.java b/app/src/main/java/com/gh/gamecenter/qa/newest/AskQuestionsNewBodyFragment.java index 7a44a82aaa..6a8f5bb606 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/newest/AskQuestionsNewBodyFragment.java +++ b/app/src/main/java/com/gh/gamecenter/qa/newest/AskQuestionsNewBodyFragment.java @@ -20,6 +20,7 @@ import com.gh.gamecenter.baselist.NormalListViewModel; import com.gh.gamecenter.eventbus.EBReuse; import com.gh.gamecenter.eventbus.EBTypeChange; import com.gh.gamecenter.manager.UserManager; +import com.gh.gamecenter.qa.BbsType; import com.gh.gamecenter.qa.CommunityFragment; import com.gh.gamecenter.qa.entity.Questions; import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity; @@ -71,7 +72,7 @@ public class AskQuestionsNewBodyFragment extends ListFragment startActivity(QuestionEditActivity.Companion.getIntent(getContext())); + mOnLoginListener = () -> startActivity(QuestionEditActivity.Companion.getIntent(getContext(), BbsType.GAME_BBS)); } @Override diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt index 60a988cb5a..1b2121a1db 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt @@ -5,9 +5,6 @@ import android.app.Activity import android.app.Dialog import android.content.Context import android.content.Intent -import android.graphics.Color -import android.graphics.LinearGradient -import android.graphics.Shader import android.os.Bundle import android.os.Message import android.text.Editable @@ -20,106 +17,103 @@ import androidx.core.widget.addTextChangedListener import androidx.databinding.DataBindingUtil import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders -import com.gh.base.ToolBarActivity +import com.gh.base.BaseRichEditorActivity import com.gh.base.fragment.WaitingDialogFragment import com.gh.common.AppExecutor import com.gh.common.util.* -import com.gh.common.view.RichEditor -import com.gh.common.view.SpacingItemDecoration import com.gh.gamecenter.R -import com.gh.gamecenter.SuggestionActivity.MEDIA_STORE_REQUEST import com.gh.gamecenter.databinding.ActivityQuestionsEditBinding -import com.gh.gamecenter.entity.CommunityEntity -import com.gh.gamecenter.entity.MyVideoEntity -import com.gh.gamecenter.entity.NotificationUgc -import com.gh.gamecenter.entity.Permissions +import com.gh.gamecenter.entity.* import com.gh.gamecenter.eventbus.EBReuse +import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mvvm.Status +import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity import com.gh.gamecenter.qa.article.draft.ArticleDraftActivity import com.gh.gamecenter.qa.article.edit.ArticleEditActivity -import com.gh.gamecenter.qa.dialog.ChooseForumDialogFragment -import com.gh.gamecenter.qa.editor.VideoActivity -import com.gh.gamecenter.qa.entity.CommunityVideoEntity +import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment +import com.gh.gamecenter.qa.dialog.ChooseForumActivity +import com.gh.gamecenter.qa.editor.GameActivity import com.gh.gamecenter.qa.entity.QuestionDraftEntity import com.gh.gamecenter.qa.entity.QuestionsDetailEntity import com.gh.gamecenter.qa.questions.draft.QuestionDraftActivity -import com.gh.gamecenter.qa.questions.edit.pic.QuestionsEditPicAdapter import com.gh.gamecenter.qa.questions.edit.tip.QuestionTitleTipAdapter -import com.lightgame.utils.Util_System_Keyboard +import com.gh.gamecenter.qa.video.publish.VideoPublishFragment import com.lightgame.utils.Utils -import com.zhihu.matisse.Matisse import org.greenrobot.eventbus.EventBus import org.json.JSONObject -import kotlin.math.abs /** * Created by khy on 28/04/18. */ -class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { +class QuestionEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver { private lateinit var mBinding: ActivityQuestionsEditBinding - private lateinit var mViewModel: QuestionEditViewModel - private var mProcessingDialog: WaitingDialogFragment? = null private var mUploadImageCancelDialog: Dialog? = null - private var mKeyboardHeightProvider: KeyboardHeightProvider? = null - private lateinit var picAdapter: QuestionsEditPicAdapter private lateinit var mMenuDraft: MenuItem private lateinit var mMenuPost: MenuItem - private var mIsExtendedKeyboardShow = false - private var mOffset = 0 private var mTagsSelectFragment: TagsSelectFragment? = null private var mPostDraftsCount: Int = 0 - private val mSaveTitleKey = "title" private val mSaveContentKey = "content" - private val mSaveImagesKey = "images" override fun getLayoutId(): Int { return R.layout.activity_questions_edit } + override fun mtaEventName(): String { + return "" + } + @SuppressLint("Recycle") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - - if (requestCode == MEDIA_STORE_REQUEST && resultCode == RESULT_OK) { - val selectedPics = Matisse.obtainResult(data) - mViewModel.validatePic(selectedPics) - } else if (requestCode == VideoActivity.INSERT_VIDEO_CODE && resultCode == RESULT_OK) { - val videoEntity = data?.getParcelableExtra(MyVideoEntity::class.java.simpleName) - if (videoEntity != null) { - mViewModel.videoLiveData.postValue(CommunityVideoEntity( - id = videoEntity.id, - url = videoEntity.url, - poster = videoEntity.poster, - duration = RichEditor.formatVideoDuration(videoEntity.length), - status = videoEntity.status)) - } - } else if (requestCode == QUESTION_DRAFT_REQUEST_CODE && resultCode == RESULT_OK) { - val draftEntity = data?.getParcelableExtra(QuestionDraftEntity::class.java.simpleName) + if (data == null || resultCode != Activity.RESULT_OK) return + if (requestCode == QUESTION_DRAFT_REQUEST_CODE) { + val draftEntity = data.getParcelableExtra(QuestionDraftEntity::class.java.simpleName) if (draftEntity != null) { mViewModel.questionDraftEntity = draftEntity setQuestionDraft(draftEntity) mViewModel.getQuestionDraftContent(draftEntity.id) } + } else if (requestCode == ChooseForumActivity.CHOOSE_FORUM_REQUEST) { + val community = data.getParcelableExtra(EntranceUtils.KEY_COMMUNITY_DATA) + mViewModel.communityEntity = community + mViewModel.type = community?.type ?: "" + if (mViewModel.questionEntity != null) { + mViewModel.questionEntity?.community?.id = community?.id ?: "" + mViewModel.questionEntity?.community?.name = community?.name ?: "" + } + if (mViewModel.type == BbsType.GAME_BBS.value) { + mViewModel.gameEntity = null + } + setForumName() + mBinding.vm = mViewModel + } else if (requestCode == VideoPublishFragment.REQUEST_GAME_CODE) { + val game = data.getParcelableExtra(GameEntity::class.java.simpleName) + if (game != null) { + mViewModel.gameEntity = game + setForumName() + } } } override fun handleMessage(msg: Message) { super.handleMessage(msg) if (msg.what == 1) { - if (mViewModel.communityEntity != null - && !mViewModel.title.isNullOrEmpty() + if (!mViewModel.title.isNullOrEmpty() && !mViewModel.content.isNullOrEmpty()) { + mViewModel.title = mBinding.questionseditTitle.text.toString() + mViewModel.content = getReplaceRealContent() mViewModel.saveQuestionDraft(SaveDraftType.AUTO) } mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong()) } } + @SuppressLint("ClickableViewAccessibility") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -127,24 +121,78 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { mToolbar.navigationIcon = null mMenuDraft = mToolbar.menu.findItem(R.id.menu_draft) mMenuPost = mToolbar.menu.findItem(R.id.menu_question_post) - mViewModel = ViewModelProviders.of(this).get(QuestionEditViewModel::class.java) if (savedInstanceState != null) { val cacheTitle = savedInstanceState.getString(mSaveTitleKey) val cacheContent = savedInstanceState.getString(mSaveContentKey) - val cacheImage = savedInstanceState.getStringArrayList(mSaveImagesKey) if (!cacheTitle.isNullOrEmpty()) mViewModel.title = cacheTitle if (!cacheContent.isNullOrEmpty()) mViewModel.content = cacheContent - if (cacheImage != null && cacheImage.isNotEmpty()) { - mViewModel.picList.value = cacheImage // can not be use postValue - } } - checkPostButtonEnable() - mBinding = DataBindingUtil.bind(mContentView)!! + checkPostButtonEnable() mBinding.questionseditTitle.setOnEditorActionListener { _, _, e -> e?.keyCode == KeyEvent.KEYCODE_ENTER; } mBinding.vm = mViewModel - mKeyboardHeightProvider = KeyboardHeightProvider(this) - mBinding.root.post { mKeyboardHeightProvider?.start() } + mBinding.editorInsertContainer.editorFont.visibility = View.GONE + mBinding.editorInsertContainer.editorLink.visibility = View.GONE + + // Navigation + when { + mViewModel.isModeratorPatch -> setNavigationTitle("修改问题") + mViewModel.questionEntity != null -> setNavigationTitle("修改问题") + else -> setNavigationTitle("发提问") + } + + mViewModel.initCommunityDataAndDraft() + + // TitleTip + if (mViewModel.questionEntity == null) { + val titleTipAdapter = QuestionTitleTipAdapter(this, mBinding.questionseditTitle, mViewModel.communityEntity?.id) + mBinding.questionseditTitle.setAdapter(titleTipAdapter) + } + + // TitleEdit + mBinding.questionseditTitle.addTextChangedListener(LimitTextWatcher(mBinding.questionseditTitle)) + mBinding.questionseditTitle.filters = arrayOf(TextHelper.getFilter(50, "标题最多50个字")) + mBinding.questionseditTitle.addTextChangedListener { + checkPostButtonEnable() + } + + mBinding.richEditor.setOnTextChangeListener { t -> + if (t.contains(" + closeExtendedKeyboard() + false + } + + mBinding.questionseditTitle.setOnFocusChangeListener { _, hasFocus -> + controlEditorInsertContainerEnabled(!hasFocus) + } + mBinding.questionseditTitle.requestFocus() + + mBinding.backBtn.setOnClickListener { + onBackPressed() + } + mBinding.chooseForumTv.setOnClickListener { + showSelectGameDialog() + } + mBinding.chooseActivityContainer.setOnClickListener { + ChooseActivityDialogFragment.show(this, mViewModel.selectActivityLabelEntity?.id) { + mViewModel.selectActivityLabelEntity = it + mBinding.activityTitle.text = it.name + mBinding.activityTitle.setTextColor(R.color.text_FA8500.toColor()) + } + } + + // 增加提问时, 如果searchKey不为空 光标跳到最后 + mBaseHandler.postDelayed({ + mBinding.questionseditTitle.setSelection(mBinding.questionseditTitle.text.toString().length) + }, 50) if (intent != null) { val communityEntity = intent.getParcelableExtra(CommunityEntity::class.java.simpleName) @@ -166,123 +214,53 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { searchKey = searchKey.substring(0, QuestionEditViewModel.QUESTION_TITLE_MAX_LENGTH) if (mViewModel.title.isNullOrEmpty()) mViewModel.title = searchKey mViewModel.isFromSearch = intent.getBooleanExtra(QuestionEditViewModel.QUESTION_FORM_SEARCH, false) + mViewModel.type = intent?.getStringExtra(BbsType::class.java.simpleName) ?: "" mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong()) + if (communityEntity != null) { + mViewModel.communityEntity = communityEntity + setForumName() + if (mViewModel.type == BbsType.GAME_BBS.value) { + mBinding.chooseForumTv.isEnabled = false + mBinding.chooseForumTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) + } + } } } - - if (communityEntity != null) { - mViewModel.communityEntity = communityEntity - setForumName() - mBinding.chooseForumTv.isEnabled = false - mBinding.chooseForumTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - } } - - val mLinearGradient = LinearGradient(0f, 0f, mBinding.tipsTitleTv.paint.textSize * mBinding.tipsTitleTv.text.length.toFloat(), 0f, Color.parseColor("#25B4E8"), Color.parseColor("#2496FF"), Shader.TileMode.CLAMP) - mBinding.tipsTitleTv.paint.shader = mLinearGradient - - // Navigation - when { - mViewModel.isModeratorPatch -> setNavigationTitle("修改问题") - mViewModel.questionEntity != null -> setNavigationTitle("修改问题") - else -> setNavigationTitle("发布提问") - } - - mViewModel.initCommunityDataAndDraft() - mViewModel.getDefaultTag(null) - - // forum - mBinding.chooseForumTv.setOnClickListener { - showSelectGameDialog() - } - - // TitleTip - if (mViewModel.questionEntity == null) { - val titleTipAdapter = QuestionTitleTipAdapter(this, mBinding.questionseditTitle, mViewModel.communityEntity?.id) - mBinding.questionseditTitle.setAdapter(titleTipAdapter) - } - - // TitleEdit - mBinding.questionseditTitle.addTextChangedListener(LimitTextWatcher(mBinding.questionseditTitle)) - mBinding.questionseditTitle.filters = arrayOf(TextHelper.getFilter(50, "标题最多50个字")) - mBinding.questionseditTitle.addTextChangedListener { - checkPostButtonEnable() - } - mBinding.questionseditTitle.setOnTouchListener { v, event -> - closeExtendedKeyboard() - false - } - - // ContentEdit - mBinding.questionseditContent.filters = arrayOf(TextHelper.getFilter(300, "内容最多300个字")) - mBinding.questionseditContent.addTextChangedListener { - mBinding.editorTextNumTv.text = "${mBinding.questionseditContent.text.length}/300" - checkPostButtonEnable() - } - mBinding.questionseditContent.setOnTouchListener { v, event -> - closeExtendedKeyboard() - false - } - - - // Pic List - picAdapter = QuestionsEditPicAdapter(this, mViewModel) - mBinding.suggestPicRv.layoutManager = object : androidx.recyclerview.widget.GridLayoutManager(this, 5) { - override fun canScrollVertically(): Boolean { - return true - } - } - - mBinding.questionseditTitle.setOnFocusChangeListener { v, hasFocus -> - if (hasFocus) { - mBinding.editorInsertContainer.editorInsertDetailContainer.visibility = View.GONE - } - } - mBinding.questionseditTitle.requestFocus() - mBinding.questionseditContent.setOnFocusChangeListener { v, hasFocus -> - if (hasFocus) { - mBinding.editorInsertContainer.editorInsertDetailContainer.visibility = View.GONE - } - } - - mBinding.backBtn.setOnClickListener { - onBackPressed() - } - - mBinding.suggestPicRv.addItemDecoration(SpacingItemDecoration(bottom = DisplayUtils.dip2px(10f))) - mBinding.suggestPicRv.adapter = picAdapter - initEditorInsertContainer() - // 增加提问时, 如果searchKey不为空 光标跳到最后 - mBaseHandler.postDelayed({ - mBinding.questionseditTitle.setSelection(mBinding.questionseditTitle.text.toString().length) - }, 50) observeData() } + override fun provideViewModel(): QuestionEditViewModel { + mViewModel = ViewModelProviders.of(this).get(QuestionEditViewModel::class.java) + return mViewModel + } + private fun setPatchContent(detailEntity: QuestionsDetailEntity) { mMenuDraft.isVisible = false + mViewModel.type = detailEntity.type mViewModel.questionDraftEntity = detailEntity.me.questionDraft mViewModel.questionEntity = detailEntity mViewModel.communityEntity = detailEntity.community mViewModel.communityEntity?.icon = detailEntity.community.game?.getIcon() mViewModel.communityEntity?.iconSubscript = detailEntity.community.game?.iconSubscript + mViewModel.gameEntity = detailEntity.gameEntity + if (detailEntity.tagActivityId.isNotEmpty() && detailEntity.tagActivityName.isNotEmpty()) { + mViewModel.selectActivityLabelEntity = ActivityLabelEntity(detailEntity.tagActivityId, detailEntity.tagActivityName) + mBinding.activityTitle.text = detailEntity.tagActivityName + mBinding.activityTitle.setTextColor(R.color.text_FA8500.toColor()) + } mViewModel.isModeratorPatch = intent.getBooleanExtra(EntranceUtils.KEY_QUESTION_MODERATOR_PATCH, false) // 编辑问题时可能存在草稿 if (mViewModel.questionDraftEntity != null) { mViewModel.title = mViewModel.questionDraftEntity?.title - mViewModel.picList.postValue(ArrayList(mViewModel.questionDraftEntity?.images - ?: arrayListOf())) - val videos = mViewModel.questionDraftEntity?.videos - if (!videos.isNullOrEmpty()) mViewModel.videoLiveData.postValue(videos[0]) mViewModel.getQuestionDraftContent(mViewModel.questionDraftEntity?.id ?: "") } else { mViewModel.title = detailEntity.title mViewModel.content = detailEntity.description - mViewModel.picList.postValue(ArrayList(detailEntity.images)) - val videos = detailEntity.videos - if (videos.isNotEmpty()) mViewModel.videoLiveData.postValue(detailEntity.videos[0]) + mBinding.questionseditTitle.setText(detailEntity.title) + setEditHtml(detailEntity.description) } setForumName() @@ -292,13 +270,17 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { mViewModel.communityEntity = draftEntity.bbs mViewModel.communityEntity?.icon = draftEntity.bbs?.game?.getIcon() mViewModel.communityEntity?.iconSubscript = draftEntity.bbs?.game?.iconSubscript + mViewModel.type = draftEntity.type + if (draftEntity.tagActivityId.isNotEmpty() && draftEntity.tagActivityName.isNotEmpty()) { + mViewModel.selectActivityLabelEntity = ActivityLabelEntity(draftEntity.tagActivityId, draftEntity.tagActivityName) + mBinding.activityTitle.text = draftEntity.tagActivityName + mBinding.activityTitle.setTextColor(R.color.text_FA8500.toColor()) + } + setEditHtml(draftEntity.description) + mViewModel.gameEntity = draftEntity.gameEntity mViewModel.title = draftEntity.title mViewModel.content = draftEntity.description - mViewModel.picList.postValue(draftEntity.images) - val videos = draftEntity.videos - if (videos.isNotEmpty()) mViewModel.videoLiveData.postValue(videos[0]) mBinding.questionseditTitle.setText(mViewModel.title) - mBinding.questionseditContent.setText(mViewModel.content) setForumName() } @@ -344,9 +326,13 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { val intent = Intent() intent.putExtra(QuestionDraftEntity::class.java.simpleName, mViewModel.questionDraftEntity) setResult(Activity.RESULT_OK, intent) - Utils.toast(this, "已保存!") + if (mViewModel.checkIsAllUploadedAndToast()) { + Utils.toast(this, "已保存!") + } } else { - Utils.toast(this, "问题已保存到草稿箱") + if (mViewModel.checkIsAllUploadedAndToast()) { + Utils.toast(this, "问题已保存到草稿箱") + } } EventBus.getDefault().post(EBReuse(ArticleEditActivity.ARTICLE_DRAFT_CHANGE_TAG)) finish() @@ -374,7 +360,9 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { } }) mViewModel.questionDraftsContent.observe(this, Observer { - mViewModel.questionDraftEntity = it + mViewModel.questionDraftEntity?.bbs = it.bbs + mViewModel.questionDraftEntity?.gameEntity = it.gameEntity + mViewModel.questionDraftEntity?.description = it.description setQuestionDraft(it) }) @@ -391,7 +379,6 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { , "图片正在上传中,确定取消吗?" , "确定", "取消", { mViewModel.uploadImageSubscription!!.dispose() - mViewModel.successfullyUploadedPicList.clear() mUploadImageCancelDialog?.dismiss() mProcessingDialog?.dismiss() }, null) @@ -403,129 +390,12 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { mProcessingDialog?.dismiss() } }) - mViewModel.selectedTagsChange.observe(this, Observer { - val selectedTags = mViewModel.selectedTags - if (selectedTags.isEmpty()) { - mBinding.editorInsertContainer.apply { - addLabelTv.text = "添加标签" - labelNumTv.visibility = View.GONE - if (tagsContainer.visibility == View.GONE) { - addLabelTv.setTextColor(ContextCompat.getColor(this@QuestionEditActivity, R.color.text_666666)) - addLabelTv.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(this@QuestionEditActivity, R.drawable.ic_add_label), null, null, null) - } else { - addLabelTv.setTextColor(ContextCompat.getColor(this@QuestionEditActivity, R.color.theme_font)) - addLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - } - } - - } else { - mBinding.editorInsertContainer.apply { - addLabelTv.text = "标签" - labelNumTv.goneIf(tagsContainer.visibility == View.VISIBLE) - labelNumTv.text = selectedTags.size.toString() - addLabelTv.setTextColor(ContextCompat.getColor(this@QuestionEditActivity, R.color.theme_font)) - addLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - } - } - }) - - mViewModel.picList.observe(this, Observer { it -> - it?.let { picAdapter.notifyPicList(it) } - }) - - mViewModel.videoLiveData.observe(this, Observer { - mBinding.editorInsertContainer.editorLinkVideo.isEnabled = mViewModel.videoLiveData.value == null - picAdapter.notifyDataSetChanged() - }) mViewModel.notSelectForum.observe(this, Observer { if (it) showSelectGameDialog() }) } - private fun changeAddLabel(isLabelContainerShow: Boolean) { - if (isLabelContainerShow) { - mBinding.editorInsertContainer.apply { - labelNumTv.visibility = View.GONE - labelArrow.visibility = View.VISIBLE - addLabelTv.setTextColor(ContextCompat.getColor(this@QuestionEditActivity, R.color.theme_font)) - addLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - addLabelContainer.background = ContextCompat.getDrawable(this@QuestionEditActivity, R.drawable.bg_editor_insert_add_label) - } - - } else { - val selectedLabel = mViewModel.selectedTags.size - if (selectedLabel == 0) { - mBinding.editorInsertContainer.apply { - addLabelTv.text = "添加标签" - labelNumTv.visibility = View.GONE - addLabelTv.setTextColor(ContextCompat.getColor(this@QuestionEditActivity, R.color.text_666666)) - addLabelTv.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(this@QuestionEditActivity, R.drawable.ic_add_label), null, null, null) - } - } else { - mBinding.editorInsertContainer.apply { - addLabelTv.text = "标签" - labelNumTv.visibility = View.VISIBLE - labelNumTv.text = selectedLabel.toString() - addLabelTv.setTextColor(ContextCompat.getColor(this@QuestionEditActivity, R.color.theme_font)) - addLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) - } - } - mBinding.editorInsertContainer.apply { - labelArrow.visibility = View.GONE - addLabelContainer.background = ContextCompat.getDrawable(this@QuestionEditActivity, R.drawable.border_round_stroke_eee_999) - } - } - } - - private fun initEditorInsertContainer() { - mBinding.editorInsertContainer.apply { - editorFont.visibility = View.GONE - editorLink.visibility = View.GONE - addLabelContainer.visibility = View.VISIBLE - addLabelContainer.setOnClickListener { - controlInsertDetailContainer() - } - editorImage.setOnClickListener { - picAdapter.selectPic() - } - editorLinkVideo.setOnClickListener { - PermissionHelper.checkStoragePermissionBeforeAction(this@QuestionEditActivity, - object : EmptyCallback { - override fun onCallback() { - MtaHelper.onEvent("发表问题", "插入链接", "插入视频") - startActivityForResult(VideoActivity.getIntent(this@QuestionEditActivity), VideoActivity.INSERT_VIDEO_CODE) - } - }) - } - } - } - - fun closeExtendedKeyboard() { - mBinding.editorInsertContainer.editorInsertDetailContainer.visibility = View.GONE - mIsExtendedKeyboardShow = false - changeAddLabel(false) - } - - fun controlInsertDetailContainer() { - mBinding.editorInsertContainer.apply { - val isShouldDelay = if (editorInsertDetailContainer.visibility == View.GONE) { - Util_System_Keyboard.hideSoftKeyboard(this@QuestionEditActivity) - changeAddLabel(true) - true - } else { - Util_System_Keyboard.showSoftKeyboard(this@QuestionEditActivity) - changeAddLabel(false) - false - } - editorInsertDetailContainer.postDelayed({ - editorInsertDetailContainer.visibility = if (editorInsertDetailContainer.visibility == View.GONE) View.VISIBLE else View.GONE - tagsContainer.visibility = if (editorInsertDetailContainer.visibility == View.VISIBLE) View.VISIBLE else View.GONE - mIsExtendedKeyboardShow = editorInsertDetailContainer.visibility == View.VISIBLE - }, if (isShouldDelay) 200 else 0L) - } - } - override fun onMenuItemClick(menuItem: MenuItem?): Boolean { if (menuItem?.itemId == R.id.menu_question_post) { if (mViewModel.isModeratorPatch) { @@ -541,7 +411,7 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { }, null) } } else { - if (mViewModel.checkTitleAndLoadTitleTag()) { + if (mViewModel.checkTitleAndLoadTitleTag(mIsKeyBoardShow)) { mTagsSelectFragment?.postQuestion() } } @@ -555,6 +425,8 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { } private fun checkPostButtonEnable() { + mViewModel.title = mBinding.questionseditTitle.text.toString() + mViewModel.content = getReplaceRealContent() val isEnabled = mViewModel.checkData() mMenuPost.actionView.alpha = if (isEnabled) 1f else 0.6f } @@ -563,25 +435,19 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { mProcessingDialog?.dismissAllowingStateLoss() mProcessingDialog = null super.onDestroy() - mKeyboardHeightProvider?.close() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(mSaveTitleKey, mViewModel.title) outState.putString(mSaveContentKey, mViewModel.content) - outState.putStringArrayList(mSaveImagesKey, mViewModel.picList.value as java.util.ArrayList?) } private fun showSelectGameDialog() { - ChooseForumDialogFragment.show(this) { - mViewModel.communityEntity = it - if (mViewModel.questionEntity != null) { - mViewModel.questionEntity?.community?.id = it.id - mViewModel.questionEntity?.community?.name = it.name - } - setForumName() - mBinding.vm = mViewModel + if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + startActivityForResult(GameActivity.getIntent(this, GameActivity.SELECT_GAME_TITLE), VideoPublishFragment.REQUEST_GAME_CODE) + } else { + ChooseForumActivity.startChooseForumActivity(this) } } @@ -619,14 +485,13 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { // 退出提示 override fun handleBackPressed(): Boolean { - if (mIsExtendedKeyboardShow) { - mBinding.editorInsertContainer.editorInsertDetailContainer.visibility = View.GONE - mIsExtendedKeyboardShow = false - changeAddLabel(false) + if (super.handleBackPressed()) { return true } - // 需要检查的内容,其中任意一个不为空都要打开提示弹窗 - val imgList = mViewModel.picList.value + + if (TextUtils.isEmpty(UserManager.getInstance().token)) { + return false + } if (mViewModel.isModeratorPatch) { if (checkSameFromQuestionData()) { @@ -640,10 +505,12 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { //问题发布 if (mViewModel.questionEntity == null && mViewModel.questionDraftEntity == null) { - if (!imgList.isNullOrEmpty() || !mViewModel.title.isNullOrEmpty() || !mViewModel.content.isNullOrEmpty()) { + if (!mBinding.questionseditTitle.text.isNullOrEmpty() || !mBinding.richEditor.html.isNullOrEmpty()) { DialogUtils.showNewAlertDialog(this, "提示", "是否保存内容再退出?", "不保存", "保存并退出", Gravity.CENTER, true, { finish() }, { + mViewModel.title = mBinding.questionseditTitle.text.toString() + mViewModel.content = getReplaceRealContent() mViewModel.saveQuestionDraft(SaveDraftType.EXIT) }) return true @@ -652,8 +519,8 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { //问题编辑,需要判断是否修改过 if (mViewModel.questionEntity != null && mViewModel.questionDraftEntity == null) { - return if (mViewModel.questionEntity?.title != mViewModel.title - || mViewModel.questionEntity?.description != mViewModel.content) { + return if (mViewModel.questionEntity?.title != mBinding.questionseditTitle.text.toString() + || mViewModel.questionEntity?.description != mBinding.richEditor.html) { showBackDialog() true } else false @@ -667,15 +534,17 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { if (draftEntity.title.isEmpty() && draftEntity.description.isEmpty()) return true if (saveType == SaveDraftType.SKIP) { //判断是否修改了草稿,修改了需自动保存无需提示 - if (draftEntity.title != mViewModel.title - || draftEntity.description != mViewModel.content) { + if (draftEntity.title != mBinding.questionseditTitle.text.toString() + || draftEntity.description != mBinding.richEditor.html) { + mViewModel.title = mBinding.questionseditTitle.text.toString() + mViewModel.content = getReplaceRealContent() mViewModel.saveQuestionDraft(SaveDraftType.AUTO) return true } } else if (saveType == SaveDraftType.EXIT) { //退出页面需判断是否修改了草稿,修改了需弹窗提示 - if (draftEntity.title != mViewModel.title - || draftEntity.description != mViewModel.content) { + if (draftEntity.title != mBinding.questionseditTitle.text.toString() + || draftEntity.description != mBinding.richEditor.html) { showBackDialog() return false } @@ -687,6 +556,8 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { DialogUtils.showNewAlertDialog(this, "提示", "是否保存修改内容用于下次编辑?", "不保存", "保存并退出", Gravity.CENTER, true, { finish() }, { + mViewModel.title = mBinding.questionseditTitle.text.toString() + mViewModel.content = getReplaceRealContent() mViewModel.saveQuestionDraft(SaveDraftType.EXIT) }) } @@ -699,9 +570,6 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { if (mViewModel.content != it.description) { return false } - if (!mViewModel.picList.value.checkSameFromStringArray(it.images)) { - return false - } return true } return false @@ -709,12 +577,25 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { private fun setForumName() { if (mViewModel.communityEntity != null) { - mBinding.chooseForumTv.text = mViewModel.communityEntity?.name - mBinding.forumIconView.visibility = View.VISIBLE - mBinding.forumIconView.displayGameIcon(mViewModel.communityEntity?.icon, mViewModel.communityEntity?.iconSubscript) - mBinding.forumContainer.background = ContextCompat.getDrawable(this, R.drawable.bg_shape_f8_radius_4) - mBinding.chooseForumTv.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(this, R.drawable.ic_article_edit_choose_forum_arrow_gray), null) - mBinding.chooseForumTv.setTextColor(ContextCompat.getColor(this, R.color.text_333333)) + if (mViewModel.type == BbsType.GAME_BBS.value) { + mBinding.chooseForumTv.text = mViewModel.communityEntity?.name + mBinding.forumIconView.displayGameIcon(mViewModel.communityEntity?.icon, mViewModel.communityEntity?.iconSubscript) + setForumUI() + } else if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + if (mViewModel.gameEntity == null) { + mBinding.chooseForumTv.text = "选择游戏" + } else { + mBinding.chooseForumTv.text = mViewModel.gameEntity?.name + mBinding.forumIconView.displayGameIcon(mViewModel.gameEntity?.icon, mViewModel.gameEntity?.iconSubscript) + setForumUI() + } + } + } else { + if (mViewModel.type == BbsType.GAME_BBS.value) { + mBinding.chooseForumTv.text = "选择论坛" + } else if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + mBinding.chooseForumTv.text = "选择游戏" + } } val beginTransaction = supportFragmentManager.beginTransaction() @@ -726,24 +607,30 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { checkPostButtonEnable() } - override fun onResume() { - super.onResume() - mKeyboardHeightProvider?.setKeyboardHeightObserver(this) + private fun setForumUI() { + mBinding.forumIconView.visibility = View.VISIBLE + mBinding.forumContainer.background = ContextCompat.getDrawable(this, R.drawable.bg_shape_f8_radius_4) + mBinding.chooseForumTv.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(this, R.drawable.ic_article_edit_choose_forum_arrow_gray), null) + mBinding.chooseForumTv.setTextColor(ContextCompat.getColor(this, R.color.text_333333)) } - override fun onPause() { - super.onPause() - mKeyboardHeightProvider?.setKeyboardHeightObserver(null) - } - - - override fun onKeyboardHeightChanged(height: Int, orientation: Int) { - if (height > 0) { - changeAddLabel(false) - closeExtendedKeyboard() + private fun setEditHtml(html: String?) { + mBinding.richEditor.setHtml(html, false) + try { + mBinding.richEditor.scrollTo(0, 10000000) + } catch (e: Exception) { + e.printStackTrace() } } + private fun getReplaceRealContent(): String { + var content = mBinding.richEditor.html + for (s in mViewModel.mapImages.keys) { + content = content?.replace(FILE_HOST + s, mViewModel.mapImages[s].toString()) + } + return content + } + companion object { const val QUESTION_POSTED_TAG = "QUESTION_POSTED_TAG" const val QUESTION_DRAFT_REQUEST_CODE = 105 @@ -759,13 +646,16 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver { } // 新增问题 - fun getIntent(context: Context): Intent { - return Intent(context, QuestionEditActivity::class.java) + fun getIntent(context: Context, type: BbsType = BbsType.GAME_BBS): Intent { + val intent = Intent(context, QuestionEditActivity::class.java) + intent.putExtra(BbsType::class.java.simpleName, type.value) + return intent } - fun getIntent(context: Context, communityEntity: CommunityEntity): Intent { + fun getIntent(context: Context, communityEntity: CommunityEntity, type: String = ""): Intent { val intent = Intent(context, QuestionEditActivity::class.java) intent.putExtra(CommunityEntity::class.java.simpleName, communityEntity) + intent.putExtra(BbsType::class.java.simpleName, type) return intent } diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditViewModel.kt index 330c8a7330..c53580d038 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditViewModel.kt @@ -2,33 +2,29 @@ package com.gh.gamecenter.qa.questions.edit import android.annotation.SuppressLint import android.app.Application -import android.net.Uri -import android.provider.MediaStore import android.text.TextUtils -import androidx.lifecycle.AndroidViewModel +import android.view.Gravity import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData +import com.gh.base.BaseRichEditorViewModel +import com.gh.base.RichType import com.gh.base.fragment.WaitingDialogFragment import com.gh.common.util.* import com.gh.gamecenter.R +import com.gh.gamecenter.entity.ActivityLabelEntity import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.Permissions -import com.gh.gamecenter.entity.User import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mvvm.Resource -import com.gh.gamecenter.qa.article.edit.ArticleEditActivity -import com.gh.gamecenter.qa.entity.ArticleDraftEntity -import com.gh.gamecenter.qa.entity.CommunityVideoEntity +import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.entity.QuestionDraftEntity import com.gh.gamecenter.qa.entity.QuestionsDetailEntity import com.gh.gamecenter.retrofit.BiResponse import com.gh.gamecenter.retrofit.Response -import com.gh.gamecenter.retrofit.RetrofitManager -import com.gh.gamecenter.retrofit.service.ApiService import com.lightgame.utils.Utils import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import okhttp3.MediaType import okhttp3.RequestBody @@ -37,48 +33,33 @@ import org.greenrobot.eventbus.EventBus import org.json.JSONArray import org.json.JSONObject import retrofit2.HttpException -import java.io.File /** * Created by khy on 28/04/18. */ -class QuestionEditViewModel(application: Application) : AndroidViewModel(application) { - - private val mApiService: ApiService = RetrofitManager.getInstance(getApplication()).api +class QuestionEditViewModel(application: Application) : BaseRichEditorViewModel(application) { var communityEntity: CommunityEntity? = null var isModeratorPatch: Boolean = false var isFromSearch: Boolean = false - val processDialog = MediatorLiveData() - val titleTags = MediatorLiveData>() val postLiveData = MediatorLiveData>() val moderatorPostLiveData = MediatorLiveData>() - val videoLiveData = MutableLiveData() val notSelectForum = MutableLiveData() val postQuestionDrafts = MediatorLiveData>() val questionDraftsContent = MutableLiveData() - var uploadImageSubscription: Disposable? = null - // post data var title: String? = "" var content: String? = "" - // picList 纯粹用于展示,可以包含本地和网络地址的图片 - val picList = MediatorLiveData>() var defaultTags: MutableList = ArrayList() var questionEntity: QuestionsDetailEntity? = null var questionDraftEntity: QuestionDraftEntity? = null val selectedTags: MutableList = ArrayList() val selectedTagsChange = MediatorLiveData() - - // 以 HTTP 开头的图片列表,即最终提交给接口的 url 列表 - var successfullyUploadedPicList = arrayListOf() - - // Key 是本地 uri, Value 是 http url, 用于判断该图片是否已上传 - var picMap = hashMapOf() - + var selectActivityLabelEntity: ActivityLabelEntity? = null + var gameEntity: GameEntity? = null fun initCommunityDataAndDraft() { if (questionEntity != null) { @@ -94,8 +75,7 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica * 获取默认标签和确定当前communityId */ fun getDefaultTag(isLoadOver: (() -> Unit)?) { - mApiService - .getCommunitiesTags(communityEntity?.id) + mApi.getCommunitiesTags(communityEntity?.id) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response>() { @@ -114,27 +94,42 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica * 检查标题是否符合规则 * 根据问题标题获取相应标签(问题编辑无需获取) */ - fun checkTitleAndLoadTitleTag(): Boolean { + fun checkTitleAndLoadTitleTag(isKeyBoardShow: Boolean = false): Boolean { if (TextUtils.isEmpty(title)) { - Utils.toast(getApplication(), "标题不能为空") + ToastUtils.showToast("标题不能为空", if (isKeyBoardShow) Gravity.CENTER else -1) return false } // 检查标题长度限制 title?.trim() //title = title?.replace(" ", "") - if (title!!.length < QUESTION_TITLE_MIN_LENGTH) { - Utils.toast(getApplication(), "标题至少${QUESTION_TITLE_MIN_LENGTH}个字") + if (title!!.length < TITLE_MIN_LENGTH) { + ToastUtils.showToast("标题至少${TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) return false } + val articleContent = HtmlUtils.stripHtml(content).length + if (articleContent < MIN_TEXT_LENGTH) { + ToastUtils.showToast("正文至少${TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) + return false + } + if (articleContent > MAX_TEXT_LENGTH) { + ToastUtils.showToast("帖子最多输入${MAX_TEXT_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1) + return false + } + if (questionEntity != null && questionEntity?.title == title && questionEntity?.description == content) return false if (TextUtils.isEmpty(communityEntity?.id) || TextUtils.isEmpty(communityEntity?.name)) { - Utils.toast(getApplication(), "论坛不能为空") + ToastUtils.showToast("论坛不能为空", if (isKeyBoardShow) Gravity.CENTER else -1) notSelectForum.postValue(true) return false } + if (type == BbsType.OFFICIAL_BBS.value && gameEntity == null) { + ToastUtils.showToast("请选择游戏", if (isKeyBoardShow) Gravity.CENTER else -1) + return false + } + // 检查标题结尾是否存在问号,没则主动加上 if (title!!.length <= QUESTION_TITLE_MAX_LENGTH) { val endString = title?.substring(title!!.length - 1, title!!.length) @@ -150,8 +145,12 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica if (TextUtils.isEmpty(title)) return false // 检查标题长度限制 title?.trim() - if (title!!.length < QUESTION_TITLE_MIN_LENGTH) return false + if (title!!.length < TITLE_MIN_LENGTH) return false + val articleContent = HtmlUtils.stripHtml(content).length + if (articleContent < MIN_TEXT_LENGTH) return false + if (articleContent > MAX_TEXT_LENGTH) return false if (questionEntity != null && questionEntity?.title == title && questionEntity?.description == content) return false + if (type == BbsType.OFFICIAL_BBS.value && gameEntity == null) return false return true } @@ -177,199 +176,41 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica } } - /** - * 检查图片是否符合规则 - */ - fun validatePic(picUriList: MutableList) { - val pics = picList.value ?: arrayListOf() - for (picUri in picUriList) { - val filePathColumn = arrayOf(MediaStore.Images.Media.DATA) - - val application: Application = getApplication() - - val cursor = application.contentResolver.query(picUri, filePathColumn, null, null, null) - ?: return - val isNull = cursor.moveToFirst() - if (!isNull) { - cursor.close() - continue - } - - val columnIndex = cursor.getColumnIndex(filePathColumn[0]) - val picPath = cursor.getString(columnIndex) - cursor.close() - - val file = File(picPath) - if (file.length() > ImageUtils.getUploadFileMaxSize()) { - val maxSize = ImageUtils.getUploadFileMaxSize() / 1024 / 1024 - Utils.toast(getApplication(), "不能选择大于${maxSize}MB的图片") - continue - } - - val picList = picList.value - if (picList != null && picList.size >= PIC_MAX_AMOUNT) { - Utils.toast(getApplication(), R.string.questions_edit_maxpic_hint) - break - } - - if (pics.contains(picPath)) { - Utils.toast(getApplication(), "请不要重复上传图片") - } else { - val alreadyUploaded = picMap[picPath]?.isNotEmpty() ?: false - if (alreadyUploaded) { - pics.add(picMap[picPath]!!) - } else { - picMap[picPath] = "" - pics.add(picPath) - } - } - } - picList.postValue(pics) - } - fun uploadPicAndPostQuestion(isForce: Boolean) { - // 先将已上传的图片归类到 successfullyUploadedPicList 里去 - picList.value?.let { - for (imageUri in it) { - if (imageUri.startsWith("http")) { - successfullyUploadedPicList.add(imageUri) - } else { - val uploadedUrl = picMap[imageUri] - if ((!uploadedUrl.isNullOrEmpty()) && !successfullyUploadedPicList.contains(uploadedUrl)) { - successfullyUploadedPicList.add(uploadedUrl) - } - } - } - } - - val picToUpload = findPicToUpload() - if (picToUpload == null) { - postQuestion(isForce) - } else { - processDialog.postValue(WaitingDialogFragment.WaitingDialogData("正在准备上传图片...", true)) - uploadPic(picToUpload) { postQuestion(isForce) } - } + postQuestion(isForce) } fun uploadPicAndPatchQuestion(isPatchTags: Boolean) { - // 先将已上传的图片归类到 successfullyUploadedPicList 里去 - picList.value?.let { - for (imageUri in it) { - val uploadedUrl = picMap[imageUri] - if ((!uploadedUrl.isNullOrEmpty()) && !successfullyUploadedPicList.contains(uploadedUrl)) { - successfullyUploadedPicList.add(uploadedUrl) - } - } - } - - val picToUpload = findPicToUpload() - if (picToUpload == null) { - moderatorsPatchQuestion(isPatchTags) - } else { - processDialog.postValue(WaitingDialogFragment.WaitingDialogData("正在准备上传图片...", true)) - uploadPic(picToUpload) { moderatorsPatchQuestion(isPatchTags) } - } + moderatorsPatchQuestion(isPatchTags) } - /** - * 上传图片 - */ - fun uploadPic(uri: String, successfulAction: (() -> Unit)? = null) { - picList.value?.let { - uploadImageSubscription = UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.question, - uri, - true, - object : UploadImageUtils.OnUploadImageListener { - override fun onProgress(total: Long, progress: Long) { - processDialog.postValue(WaitingDialogFragment.WaitingDialogData("正在上传图片(${successfullyUploadedPicList.size} / ${it.size})", true)) - } - - override fun onSuccess(imageUrl: String) { - successfullyUploadedPicList.add(imageUrl) - picMap[uri] = imageUrl - - if (successfullyUploadedPicList.size == it.size) { - processDialog.postValue(WaitingDialogFragment.WaitingDialogData("正在上传图片(${successfullyUploadedPicList.size} / ${it.size})", false)) - successfulAction?.invoke() - } else { - uploadPic(findPicToUpload() ?: "", successfulAction) - } - } - - override fun onError(e: Throwable?) { - successfullyUploadedPicList.clear() - processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传中...", false)) - if (e != null && e is HttpException && e.code() == 403) { - Utils.toast(getApplication(), "图片违规") - } else { - Utils.toast(getApplication(), "图片上传失败,请检查网络") - } - } - }) - } - } - - private fun findPicToUpload(): String? { - val list = picList.value - if (list == null) { - return null - } else { - for (uri in list) { - if (!uri.startsWith("http") && picMap[uri].isNullOrEmpty()) { - return uri - } - } - return null - } - } - - /** - * 移除已选中图片 - */ - fun removePic(picPath: String) { - val list = picList.value - - successfullyUploadedPicList.remove(picMap[picPath]) - - if (list != null && list.remove(picPath)) { - picList.postValue(list) - } + private fun getRequestParams(): HashMap { + val map = hashMapOf() + map["title"] = title ?: "" + map["content"] = content ?: "" + map["type"] = type + map["tag_activity_id"] = selectActivityLabelEntity?.id ?: "" + map["bbs_id"] = communityEntity?.id ?: "" + map["game_id"] = gameEntity?.id ?: "" + map["draft_id"] = questionDraftEntity?.id ?: "" + return map } /** * 提交问题 */ private fun postQuestion(isForce: Boolean) { - val entity = QuestionsDetailEntity() - entity.title = title - entity.description = content - entity.tags = selectedTags - entity.images = successfullyUploadedPicList - val video: CommunityVideoEntity? = videoLiveData.value - if (video != null) entity.videos = arrayListOf(video) - if (!questionDraftEntity?.id.isNullOrEmpty()) { - entity.draftId = questionDraftEntity?.id ?: "" - } + val requestParams = getRequestParams() val observable = if (questionEntity != null) { - if (!isModeratorPatch) { - questionEntity?.images = entity.images - questionEntity?.title = entity.title - questionEntity?.description = entity.description - questionEntity?.tags = entity.tags - questionEntity?.videos = entity.videos - } - val body = RequestBody.create(MediaType.parse("application/json"), GsonUtils.toJson(entity)) - mApiService.patchQuestions(body, questionEntity?.id) // patch + mApi.patchQuestions(requestParams.toRequestBody(), questionEntity?.id) // patch } else { val postContent = if (isForce) { // 标题重复提示后强制提交 - val bodyJson = JSONObject(GsonUtils.toJson(entity)) - bodyJson.put("again", true) - bodyJson.toString() + requestParams["again"] = true + requestParams } else { - GsonUtils.toJson(entity) + requestParams } - val body = RequestBody.create(MediaType.parse("application/json"), postContent) - mApiService.postQuestions(body, communityEntity?.id) // add + mApi.postQuestions(postContent.toRequestBody()) // add } processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) @@ -402,7 +243,7 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica }) } - fun moderatorsPatchQuestion(isPatchTags: Boolean) { + private fun moderatorsPatchQuestion(isPatchTags: Boolean) { val bodyJson = if (isPatchTags) { val tagsJson = JSONObject() tagsJson.put("tags", JSONArray(selectedTags)) @@ -411,13 +252,16 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica val entity = QuestionsDetailEntity() entity.title = title entity.description = content - entity.images = successfullyUploadedPicList + entity.type = type + entity.gameId = gameEntity?.id ?: "" + entity.tagActivityId = selectActivityLabelEntity?.id ?: "" + entity.tagActivityName = selectActivityLabelEntity?.name ?: "" GsonUtils.toJson(entity) } processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) val body = RequestBody.create(MediaType.parse("application/json"), bodyJson) - mApiService.moderatorsPatchQuestion(body, UserManager.getInstance().userId, questionEntity?.id) + mApi.moderatorsPatchQuestion(body, UserManager.getInstance().userId, questionEntity?.id) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -425,7 +269,6 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica if (questionEntity!!.me.moderatorPermissions.updateQuestion > Permissions.REPORTER && !isPatchTags) { questionEntity?.title = title questionEntity?.description = content - questionEntity?.images = successfullyUploadedPicList } moderatorPostLiveData.postValue(Resource.success("")) processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", false)) @@ -440,11 +283,15 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica @SuppressLint("CheckResult") fun saveQuestionDraft(saveType: QuestionEditActivity.SaveDraftType) { - val body = getQuestionBody() + val requestParams = getRequestParams() + if (questionEntity != null) { + requestParams["question_id"] = questionEntity?.id ?: "" + } + val body = requestParams.toRequestBody() val observable = if (!questionDraftEntity?.id.isNullOrEmpty()) { - mApiService.updateQuestionDraft(UserManager.getInstance().userId, questionDraftEntity?.id, body) + mApi.updateQuestionDraft(UserManager.getInstance().userId, questionDraftEntity?.id, body) } else { - mApiService.addQuestionDraft(UserManager.getInstance().userId, body) + mApi.addQuestionDraft(UserManager.getInstance().userId, body) } observable .compose(singleToMain()) @@ -469,10 +316,12 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica val draftEntity = QuestionDraftEntity( communityId = communityEntity?.id ?: "", tags = selectedTags.toList(), - images = picList.value ?: arrayListOf(), - videos = if (videoLiveData.value != null) arrayListOf(videoLiveData.value!!) else arrayListOf(), title = title ?: "", - description = content ?: "" + description = content ?: "", + type = type, + gameId = gameEntity?.id ?: "", + tagActivityId = selectActivityLabelEntity?.id ?: "", + tagActivityName = selectActivityLabelEntity?.name ?: "" ) if (questionEntity != null) { draftEntity.questionId = questionEntity?.id ?: "" @@ -483,7 +332,7 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica @SuppressLint("CheckResult") fun getQuestionDraftContent(draftId: String) { processDialog.postValue(WaitingDialogFragment.WaitingDialogData("加载中...", true)) - mApiService.getQuestionDraft(UserManager.getInstance().userId, draftId) + mApi.getQuestionDraft(UserManager.getInstance().userId, draftId) .compose(singleToMain()) .subscribe(object : BiResponse() { override fun onSuccess(data: QuestionDraftEntity) { @@ -498,11 +347,10 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica }) } + override fun getRichType(): RichType = RichType.QUESTION + companion object { const val QUESTION_TAG_MAX_COUNT = 5 - const val PIC_MAX_AMOUNT = 30 - const val QUESTION_TITLE_MIN_LENGTH = 6 - const val QUESTION_CONTENT_MAX_LENGTH = 300 const val QUESTION_TITLE_MAX_LENGTH = 50 const val QUESTION_DRAFT_KEY = "questionDraftKey" diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/TagsSelectFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/TagsSelectFragment.kt index b6429d9921..ed2694cf7b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/TagsSelectFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/TagsSelectFragment.kt @@ -26,6 +26,7 @@ import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mvvm.Status import com.gh.gamecenter.qa.entity.QuestionsDetailEntity import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity import com.google.android.flexbox.FlexboxLayout import com.lightgame.utils.Util_System_Keyboard import kotterknife.bindView @@ -78,9 +79,9 @@ class TagsSelectFragment : BaseFragment() { mViewModel?.isModeratorPatch = isModeratorPatch mViewModel?.questionEntity = arguments?.getParcelable(QuestionsDetailEntity::class.java.simpleName) - mViewModel?.getDefaultTag(isLoadOver = { - initTags() - }) +// mViewModel?.getDefaultTag(isLoadOver = { +// initTags() +// }) mViewModel?.moderatorPostLiveData?.observe(this, Observer { if (it?.status == Status.SUCCESS) { @@ -125,11 +126,13 @@ class TagsSelectFragment : BaseFragment() { activity?.setResult(Activity.RESULT_OK, data) } else { val data = JSONObject(it.data ?: "") - startActivity(QuestionsDetailActivity.getIntent(context, data.getString("_id"), - "", "问题编辑")) + startActivity(NewQuestionDetailActivity.getIntent(requireContext(), data.getString("_id") + ?: "", mEntrance, "发布问题")) activity?.setResult(Activity.RESULT_OK) } - toast("发布成功") + if (mViewModel?.checkIsAllUploadedAndToast() == true) { + toast("发布成功") + } activity?.finish() AppExecutor.uiExecutor.executeWithDelay(Runnable { NotificationHelper.showNotificationHintDialog(NotificationUgc.QUESTION) @@ -152,7 +155,7 @@ class TagsSelectFragment : BaseFragment() { }, { MtaHelper.onEvent("提交相似问题弹窗", UserManager.getInstance().community.name, "问题相似-去看看") - val intent = QuestionsDetailActivity.getIntent(context, data?.questionId, mEntrance, "相似问题") + val intent = NewQuestionDetailActivity.getIntent(requireContext(), data?.questionId?:"", mEntrance, "相似问题") context?.startActivity(intent) }) } @@ -165,7 +168,7 @@ class TagsSelectFragment : BaseFragment() { null, "去看看", null, { MtaHelper.onEvent("提交重复问题弹窗", UserManager.getInstance().community.name, "问题重复-去看看") - val intent = QuestionsDetailActivity.getIntent(context, data?.questionId, mEntrance, "重复问题") + val intent = NewQuestionDetailActivity.getIntent(requireContext(), data?.questionId?:"", mEntrance, "重复问题") context?.startActivity(intent) }) } @@ -185,9 +188,9 @@ class TagsSelectFragment : BaseFragment() { mCancelBtn.text = "取消" mTitle.text = "修改标签" } else { - mViewModel?.getDefaultTag(isLoadOver = { - initTags() - }) +// mViewModel?.getDefaultTag(isLoadOver = { +// initTags() +// }) } mAddTagBtn.setOnClickListener { @@ -266,7 +269,7 @@ class TagsSelectFragment : BaseFragment() { dialog.setOnDismissListener { this.view?.visibility = View.VISIBLE requireActivity().currentFocus?.clearFocus() - (requireActivity() as QuestionEditActivity).controlInsertDetailContainer() +// (requireActivity() as QuestionEditActivity).controlInsertDetailContainer() } dialog.setCancelable(false) dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/pic/QuestionsEditPicAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/pic/QuestionsEditPicAdapter.kt deleted file mode 100644 index 51398f86b0..0000000000 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/pic/QuestionsEditPicAdapter.kt +++ /dev/null @@ -1,114 +0,0 @@ -package com.gh.gamecenter.qa.questions.edit.pic - -import android.app.Activity -import android.content.Context -import android.content.pm.ActivityInfo -import android.net.Uri -import android.view.View -import android.view.ViewGroup -import com.gh.common.util.* -import com.gh.gamecenter.R -import com.gh.gamecenter.SuggestionActivity.MEDIA_STORE_REQUEST -import com.gh.gamecenter.qa.questions.edit.QuestionEditViewModel -import com.gh.gamecenter.suggest.SuggestPicViewHolder -import com.lightgame.adapter.BaseRecyclerAdapter -import com.zhihu.matisse.Matisse -import com.zhihu.matisse.MimeType -import com.zhihu.matisse.engine.impl.PicassoEngine -import java.io.File -import java.util.* - -/** - * Created by khy on 5/05/18. - */ -class QuestionsEditPicAdapter(context: Context, viewModel: QuestionEditViewModel) : BaseRecyclerAdapter(context) { - - private val mViewModel: QuestionEditViewModel = viewModel - var picList: MutableList = ArrayList() - private var mAgreePostPic: Boolean = false - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder { - val view = mLayoutInflater.inflate(R.layout.suggest_pic_item, parent, false) - return SuggestPicViewHolder(view) - } - - override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) { - if (holder is SuggestPicViewHolder) { - if (mViewModel.videoLiveData.value != null) { - if (position == 0) { - holder.icon.setImageURI(mViewModel.videoLiveData.value?.poster) - holder.videoPlayIcon?.visibility = View.VISIBLE - holder.delete.setOnClickListener { - mViewModel.videoLiveData.postValue(null) - } - } else { - bindPicUI(position - 1, holder) - } - } else { - bindPicUI(position, holder) - } - holder.delete.visibility = View.VISIBLE - } - } - - private fun bindPicUI(position: Int, holder: SuggestPicViewHolder) { - val picUrl = picList[position] - if (picUrl.startsWith("http")) { - holder.icon.setImageURI(picUrl) - } else { - holder.icon.setImageURI(Uri.fromFile(File((picUrl)))) - } - holder.videoPlayIcon?.visibility = View.GONE - holder.delete.setOnClickListener { - mViewModel.removePic(picList[position]) - } - } - - fun selectPic() { - MtaHelper.onEvent("发表问题", "上传图片", mViewModel.communityEntity?.name) - if (!ClickUtils.isFastDoubleClick(R.id.menu_question_post) - && picList.size < QuestionEditViewModel.PIC_MAX_AMOUNT - && mContext is Activity) { - MtaHelper.onEvent("发表问题", "插入图片", "插入图片") - if (NetworkUtils.isWifiOr4GOr3GConnected(mContext) || mAgreePostPic) { - selectPic(mContext as Activity) - } else { - mAgreePostPic = true - MtaHelper.onEvent("发表问题", "上传图片-移动网络提示", mViewModel.communityEntity?.name) - DialogUtils.showAlertDialog(mContext, - "警告", - "当前使用移动网络,上传图片会消耗手机流量", - "我知道了", "", - DialogUtils.ConfirmListener { - selectPic(mContext as Activity) - }, null) - } - } - } - - private fun selectPic(activity: Activity) { - PermissionHelper.checkStoragePermissionBeforeAction(activity, object : EmptyCallback { - override fun onCallback() { - Matisse.from(activity) - .choose(MimeType.ofImage()) - .showSingleMediaType(true) - .countable(true) - .addFilter(GhMatisseFilter()) - .maxSelectable(10) - .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .thumbnailScale(0.85f) - .imageEngine(PicassoEngine()) - .forResult(MEDIA_STORE_REQUEST) - } - }) - } - - override fun getItemCount(): Int { - return if (mViewModel.videoLiveData.value != null) picList.size + 1 else picList.size - } - - fun notifyPicList(picPath: MutableList) { - picList = ArrayList(picPath) - notifyDataSetChanged() - } -} diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailActivity.kt new file mode 100644 index 0000000000..6099ca64a3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailActivity.kt @@ -0,0 +1,72 @@ +package com.gh.gamecenter.qa.questions.newdetail + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.common.util.DisplayUtils +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.SpecialColumn + +class NewQuestionDetailActivity : NormalActivity() { + + override fun getLayoutId() = R.layout.activity_amway + + override fun getFragmentPlaceholderId() = R.id.placeholder + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + +// TODO 让状态栏透明 +// DisplayUtils.transparentStatusBar(this) + DisplayUtils.setLightStatusBar(this, true) + } + + override fun provideNormalIntent(): Intent { + return getTargetIntent(this, NewQuestionDetailActivity::class.java, NewQuestionDetailFragment::class.java) + } + + override fun getBusinessId(): Pair { + return Pair( + targetFragment.arguments?.getString(EntranceUtils.KEY_QUESTIONS_ID) ?: "", + targetFragment.arguments?.getParcelable(EntranceUtils.KEY_COMMUNITY_DATA)?.id + ?: "") + } + + companion object { + @JvmStatic + fun getIntent(context: Context, questionId: String, entrance: String, path: String): Intent { + val intent = Intent(context, NewQuestionDetailActivity::class.java) + intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path)) + intent.putExtra(EntranceUtils.KEY_QUESTIONS_ID, questionId) + intent.putExtra(EntranceUtils.KEY_PATH, path) + return intent + } + + /** + * 自动滚动到评论区域 + */ + @JvmStatic + fun getCommentIntent(context: Context, questionId: String, entrance: String, path: String): Intent { + val intent = Intent(context, NewQuestionDetailActivity::class.java) + intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path)) + intent.putExtra(EntranceUtils.KEY_QUESTIONS_ID, questionId) + intent.putExtra(EntranceUtils.KEY_SCROLL_TO_COMMENT_AREA, true) + intent.putExtra(EntranceUtils.KEY_PATH, path) + return intent + } + + @JvmStatic + fun getCommentIntent(context: Context, questionId: String, answerId: String, entrance: String, path: String): Intent { + val intent = Intent(context, NewQuestionDetailActivity::class.java) + intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path)) + intent.putExtra(EntranceUtils.KEY_QUESTIONS_ID, questionId) + intent.putExtra(EntranceUtils.KEY_ANSWER_ID, answerId) + intent.putExtra(EntranceUtils.KEY_SCROLL_TO_COMMENT_AREA, true) + intent.putExtra(EntranceUtils.KEY_PATH, path) + return intent + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailAdapter.kt new file mode 100644 index 0000000000..b623b40df4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailAdapter.kt @@ -0,0 +1,76 @@ +package com.gh.gamecenter.qa.questions.newdetail + +import android.content.Context +import android.text.SpannableStringBuilder +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.syncpage.ISyncAdapterHandler +import com.gh.common.util.SpanBuilder +import com.gh.common.view.CustomLinkMovementMethod +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.LoadStatus +import com.gh.gamecenter.databinding.ItemArticleDetailContentBinding +import com.gh.gamecenter.qa.answer.detail.SimpleAnswerDetailActivity +import com.gh.gamecenter.qa.article.detail.ArticleDetailContentViewHolder +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter + +class NewQuestionDetailAdapter(context: Context, var mViewModel: NewQuestionDetailViewModel, type: AdapterType, val entrance: String) : + BaseCommentAdapter(context, mViewModel, type, entrance) { + + var questionDetailVH: QuestionDetailContentViewHolder? = null + + override fun loadChange(status: LoadStatus?) { + if (status == LoadStatus.INIT) { + mIsNetworkError = false + mIsOver = false + mIsLoading = true + return + } else { + super.loadChange(status) + } + } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ITEM_QUESTION_DETAIL -> { + val binding: ItemArticleDetailContentBinding = ItemArticleDetailContentBinding.inflate(mLayoutInflater, parent, false) + QuestionDetailContentViewHolder(binding, mViewModel).apply { questionDetailVH = this } + } + + else -> super.onCreateViewHolder(parent, viewType) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + when (holder) { + is QuestionDetailContentViewHolder -> { + holder.bindView(mEntityList[position].questionDetail!!) + } + is CommentFilterViewHolder -> { + holder.bindView(questions = mViewModel.questionDetail) + } + is CommentItemViewHolder -> { + val comment = mEntityList[position].commentNormal + val spannableStringBuilder = SpannableStringBuilder() + if (comment?.type == "answer") { + val detailSpan = SpanBuilder("[查看回答详情]").click(0, 8, R.color.theme_font) { + val intent = SimpleAnswerDetailActivity.getIntent(holder.itemView.context, comment.id + ?: "", entrance, "问题详情") + holder.itemView.context.startActivity(intent) + }.build() + spannableStringBuilder.append(detailSpan) + holder.binding.contentTv.maxEms = 20 + } else { + holder.binding.contentTv.maxEms = Int.MAX_VALUE + } + spannableStringBuilder.append(comment?.content) + holder.binding.contentTv.text = spannableStringBuilder + holder.binding.contentTv.movementMethod = CustomLinkMovementMethod.getInstance() + } + else -> super.onBindViewHolder(holder, position) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt new file mode 100644 index 0000000000..29d578205b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt @@ -0,0 +1,425 @@ +package com.gh.gamecenter.qa.questions.newdetail + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.lifecycle.Lifecycle +import androidx.recyclerview.widget.RecyclerView +import com.ethanhua.skeleton.Skeleton +import com.gh.common.util.* +import com.gh.gamecenter.ImageViewerActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.SuggestionActivity +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.databinding.FragmentArticleDetailBinding +import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.MenuItemEntity +import com.gh.gamecenter.eventbus.EBDeleteDetail +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.article.detail.CommentItemData +import com.gh.gamecenter.qa.comment.CommentActivity +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter +import com.gh.gamecenter.qa.comment.base.BaseCommentFragment +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel +import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity +import com.gh.gamecenter.qa.questions.detail.QuestionsDetailFragment +import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity +import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity +import com.gh.gamecenter.suggest.SuggestType +import com.halo.assistant.HaloApp +import org.greenrobot.eventbus.EventBus +import java.util.HashSet + +class NewQuestionDetailFragment : BaseCommentFragment() { + private var mScrollToCommentArea: Boolean = false + private var mIsRecommendsContent: Boolean = false + private lateinit var mViewModel: NewQuestionDetailViewModel + private var mAdapter: NewQuestionDetailAdapter? = null + private lateinit var mBinding: FragmentArticleDetailBinding + private var mAttentionMenu: MenuItem? = null + private var mIsToolbarUserShow = false + + override fun getLayoutId() = 0 + + override fun getInflatedLayout(): View { + return FragmentArticleDetailBinding.inflate(LayoutInflater.from(requireContext()), null, false).apply { + mBinding = this + }.root + } + + override fun onCreate(savedInstanceState: Bundle?) { + mViewModel = provideListViewModel() + super.onCreate(savedInstanceState) + + mScrollToCommentArea = arguments?.getBoolean(EntranceUtils.KEY_SCROLL_TO_COMMENT_AREA, false) + ?: false + mIsRecommendsContent = arguments?.getBoolean(EntranceUtils.KEY_RECOMMENDS_CONTENTS, false) + ?: false + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initView() + initToolbar() + initObserver() + + mViewModel.getQuestionDetail() + + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode != Activity.RESULT_OK || data == null) return + if (requestCode == QuestionsDetailFragment.QUESTIONS_EDIT_REQUEST) { + data.getParcelableExtra(QuestionsDetailEntity::class.java.simpleName)?.let { + mViewModel.questionDetail = it + mAdapter?.questionDetailVH?.bindView(it) + updateView() + } + mReuseNoConn?.performClick() //重新刷新 + } else if (requestCode == ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE && resultCode == Activity.RESULT_OK) { + val imageSet = data.extras?.get(ImageViewerActivity.VIEWED_IMAGE) as HashSet + mAdapter?.questionDetailVH?.run { + if (questionImgUrlList.size > 0) { + if (imageSet.size == questionImgUrlList.size) { + binding.richEditor.replaceAllDfImage() + } else { + for (i in imageSet) { + val url = questionImgUrlList[i.toInt()] + binding.richEditor.replaceDfImageByUrl(url) + } + } + } + } + } else if (requestCode == CommentActivity.REQUEST_CODE) { + val commentCount = data.getIntExtra(CommentActivity.COMMENT_COUNT, 0) + if (commentCount != 0) { + mViewModel.questionDetail?.count?.answer = commentCount + mViewModel.commentCount = commentCount + mBinding.inputContainer.bottomCommentTv.text = mViewModel.getCommentText(mViewModel.questionDetail?.count?.answer + ?: 0, "回答") + updateFilterView() + if (EntranceUtils.ENTRANCE_WELCOME == mEntrance) { + LogUtils.uploadCommentFromWelcomeDialog() + } + mViewModel.load(LoadType.REFRESH) + } + } + } + + private fun initToolbar() { + mBinding.toolbar.inflateMenu(R.menu.menu_article_detail) + mBinding.toolbar.setNavigationOnClickListener { + onBackPressed() + } + mBinding.toolbar.menu.findItem(R.id.menu_more).setOnMenuItemClickListener { + consume { + showMoreItemDialog() + } + } + mAttentionMenu = mBinding.toolbar.menu.findItem(R.id.menu_follow) + mAttentionMenu?.isVisible = false + } + + private fun initView() { + mBinding.reuseNoneData.reuseTvNoneData.text = getString(R.string.content_delete_hint) + ViewCompat.setOnApplyWindowInsetsListener(mBinding.toolbar) { _, insets -> + (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop + insets.consumeSystemWindowInsets() + } + + mSkeletonScreen = Skeleton.bind(skeletonView).shimmer(false).load(R.layout.fragment_article_detail_skeleton).show() + + mBinding.inputContainer.replyTv.text = "说点什么吧" + mBinding.inputContainer.replyTv.setRoundedColorBackground(R.color.text_F5F5F5, 19F) + mBinding.inputContainer.replyTv.setDebouncedClickListener { + startCommentActivity() + } + mBinding.inputContainer.bottomCommentTv.text = "回答" + mBinding.inputContainer.bottomLikeTv.text = "邀请" + mBinding.inputContainer.bottomLikeIv.setImageDrawable(R.drawable.ic_question_detail_invite.toDrawable()) + updateStartButton() + mBinding.inputContainer.bottomLikeIv.setOnClickListener { + mViewModel.questionDetail?.let { + startActivity(QuestionsInviteActivity.getIntent(requireContext(), it, "$mEntrance+(问题详情)")) + } + } + mBinding.inputContainer.bottomStarIv.setOnClickListener { + debounceActionWithInterval(it.id) { + ifLogin("问题详情") { + mViewModel.postFavoriteQuestion() + } + } + } + mBinding.inputContainer.bottomCommentIv.setOnClickListener { + // 避免由于正文内容没有加载完成导致滚不到具体位置,这里要滚两次 + mListRv.scrollToPosition(1) + mListRv.post { + mListRv.smoothScrollToPosition(1) + } + } + + mReuseNoConn?.setOnClickListener { + mReuseNoConn?.visibility = View.GONE + mListLoading?.visibility = View.VISIBLE + mViewModel.getQuestionDetail() + } + } + + private fun initObserver() { + mViewModel.loadResultLiveData.observeNonNull(this) { + when (it) { + BaseCommentViewModel.LoadResult.SUCCESS -> { + updateView() + } + else -> { + if (it == BaseCommentViewModel.LoadResult.DELETED) { + mReuseNoConn?.visibility = View.GONE + mReuseNoData?.visibility = View.VISIBLE + if (mIsRecommendsContent) { + val data = Intent() + data.putExtra(EntranceUtils.KEY_ANSWER_ID, mViewModel.questionId) + requireActivity().setResult(Activity.RESULT_OK, data) + DialogUtils.showAlertDialog(requireContext(), "提示", "很抱歉,内容可能已被删除", "关闭", null, { + requireActivity().finish() + }, null) + } else { + toast(R.string.content_delete_toast) + } + + mBinding.toolbar.menu?.run { + for (i in 0 until size()) { + getItem(i).isVisible = false + } + } + } else { + mReuseNoConn?.visibility = View.VISIBLE + mReuseNoData?.visibility = View.GONE + mReuseNoConn?.setOnClickListener { + mViewModel.getQuestionDetail() + mReuseNoConn?.visibility = View.GONE + showSkeleton(true) + } + } + + mListLoading?.visibility = View.GONE + mBinding.inputContainer.bottomContainer.visibility = View.GONE + mBinding.bottomShadowView.visibility = View.GONE + showSkeleton(false) + } + } + } + + mViewModel.questionRenderedLiveData.observeNonNull(this) { + showSkeleton(false) + if (mViewModel.questionDetail?.images.isNullOrEmpty() && mScrollToCommentArea) { + mBinding.inputContainer.bottomCommentIv.performClick() + } + } + mViewModel.questionPageFinishedLiveData.observeNonNull(this) { + if (mScrollToCommentArea) { + mBinding.inputContainer.bottomCommentIv.performClick() + } + } + + mViewModel.followLiveData.observeNonNull(this) { isFollowed -> + if (isFollowed) { + toast(R.string.concern_success) + mAdapter?.questionDetailVH?.updateFollowBtn(true) + } else { + mAdapter?.questionDetailVH?.updateFollowBtn(false) + } + updateFollowMenu(isFollowed, mViewModel.questionDetail?.user?.id == UserManager.getInstance().userId) + } + mViewModel.moderatorsHideLiveData.observeNonNull(this) { + EventBus.getDefault().post(EBDeleteDetail(mViewModel.questionId)) + requireActivity().finish() + } + mViewModel.favoriteLiveData.observeNonNull(this) { + if (it) { + updateStartButton() + } + } + } + + private fun updateStartButton() { + mBinding.inputContainer.bottomStarIv.setImageDrawable(if (mViewModel.questionDetail?.me?.isQuestionFavorite == true) + R.drawable.ic_article_detail_stared_bottom_bar.toDrawable() else R.drawable.ic_article_detail_star_bottom_bar.toDrawable()) + mBinding.inputContainer.bottomStarTv.setTextColor(if (mViewModel.questionDetail?.me?.isQuestionFavorite == true) + R.color.theme_font.toColor() else R.color.text_666666.toColor()) + mBinding.inputContainer.bottomStarTv.text = if (mViewModel.questionDetail?.me?.isQuestionFavorite == true) "已收藏" else "收藏" + } + + private fun updateView() { + val questionDetail = mViewModel.questionDetail ?: return + updateStartButton() + updateFollowMenu(questionDetail.me.isFollower, questionDetail.user.id == UserManager.getInstance().userId) + + mReuseNoConn?.visibility = View.GONE + mListLoading?.visibility = View.GONE + + mBinding.inputContainer.bottomContainer.visibility = View.VISIBLE + mBinding.bottomShadowView.visibility = View.VISIBLE + mBinding.inputContainer.bottomCommentTv.text = mViewModel.getCommentText(questionDetail.count.answer, "回答") + + val community = questionDetail.community + val icon = if (!community.icon.isNullOrEmpty()) community.icon else community.game?.getIcon() + val iconSubscript = if (!community.iconSubscript.isNullOrEmpty()) community.iconSubscript else community.game?.iconSubscript + mBinding.forumGameIv.displayGameIcon(icon, iconSubscript) + ImageUtils.display(mBinding.userAvatar, questionDetail.user.icon) + mBinding.forumGameIv.visibility = View.VISIBLE + mBinding.forumTitleTv.text = questionDetail.community.name + + mListRv.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + if (!mIsToolbarUserShow && mListRv.computeVerticalScrollOffset() > 56F.dip2px()) { + mBinding.forumGameIv.visibility = View.GONE + mBinding.userAvatar.visibility = View.VISIBLE + mAttentionMenu?.isVisible = questionDetail.user.id != UserManager.getInstance().userId + mBinding.forumTitleTv.text = questionDetail.user.name + mIsToolbarUserShow = true + } else if (mIsToolbarUserShow && mListRv.computeVerticalScrollOffset() <= 56F.dip2px()) { + mBinding.forumGameIv.visibility = View.VISIBLE + mBinding.userAvatar.visibility = View.GONE + mAttentionMenu?.isVisible = false + mBinding.forumTitleTv.text = questionDetail.community.name + mIsToolbarUserShow = false + } + } + }) + } + + private fun updateFollowMenu(isFollow: Boolean, isOwner: Boolean) { + mAttentionMenu?.let { + if (isOwner) { + it.isVisible = false + return@let + } + it.isVisible = mIsToolbarUserShow + val followBtn = it.actionView.findViewById(R.id.followBtn) + followBtn.setOnClickListener { mAdapter?.questionDetailVH?.binding?.followBtn?.performClick() } + if (isFollow) { + followBtn.text = "已关注" + followBtn.setRoundedColorBackground(R.color.text_f5f5f5, 999F) + followBtn.setTextColor(R.color.text_999999.toColor()) + } else { + followBtn.text = "关注" + followBtn.setRoundedColorBackground(R.color.text_EEF5FB, 999F) + followBtn.setTextColor(R.color.theme_font.toColor()) + } + } + } + + private fun startCommentActivity() { + mViewModel.questionDetail?.let { + val intent = CommentActivity.getQuestionCommentIntent( + requireContext(), + it.id ?: "", + it.count.answer, + true, + it.community.id, + true + ) + startActivityForResult(intent, CommentActivity.REQUEST_CODE) + } + } + + override fun provideListViewModel(): NewQuestionDetailViewModel { + return viewModelProvider( + NewQuestionDetailViewModel.Factory( + HaloApp.getInstance().application, + arguments?.getString(EntranceUtils.KEY_QUESTIONS_ID) ?: "", + arguments?.getParcelable(EntranceUtils.KEY_COMMUNITY_DATA)?.id + ?: "", + arguments?.getString(EntranceUtils.KEY_ANSWER_ID) ?: "")) + } + + override fun provideListAdapter(): ListAdapter<*> { + return mAdapter + ?: NewQuestionDetailAdapter(requireContext(), mViewModel, BaseCommentAdapter.AdapterType.COMMENT, mEntrance).apply { + mAdapter = this + } + } + + override fun onBackPressed(): Boolean { + if (SyncDataBetweenPageHelper.setResultAndFinish(requireContext(), mViewModel.questionDetail)) { + return true + } + + requireActivity().finish() + return super.onBackPressed() + } + + override fun addSyncPageObserver() = true + + override fun provideSyncAdapter() = mAdapter + + private fun showMoreItemDialog() { + if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { + mViewModel.questionDetail?.let { questionEntity -> + val entities = ArrayList() + if (questionEntity.user.id != UserManager.getInstance().userId) { + entities.add(MenuItemEntity("投诉", R.drawable.icon_gamedetail_copyright)) + } + if (questionEntity.me.isModerator) { + entities.add(MenuItemEntity("编辑", R.drawable.icon_more_panel_edit)) + } + if (questionEntity.me.isModerator || questionEntity.user.id == UserManager.getInstance().userId) { + entities.add(MenuItemEntity("删除", R.drawable.icon_more_panel_delete)) + } + val shareIcon = if (questionEntity.images.isNotEmpty()) { + questionEntity.images[0] + } else { + getString(R.string.share_ghzs_logo) + } + var description = questionEntity.description + if (TextUtils.isEmpty(description)) { + description = getString(R.string.ask_share_default_summary) + } + val shareUrl = if (isPublishEnv()) { + getString(R.string.share_questions_url, questionEntity.id) + } else { + getString(R.string.share_questions_url_dev, questionEntity.id) + } + val shareUtils = ShareUtils.getInstance(activity) + shareUtils.shareParamsDetail( + activity, + shareUrl, + shareIcon, + getString(R.string.ask_share_questions_title, questionEntity.title, questionEntity.count.answer), + description, + ShareUtils.ShareEntrance.askNormal, + questionEntity.id, null) + MoreFunctionPanelDialog.showMoreDialog(requireActivity() as AppCompatActivity, entities, questionEntity.title + ?: "", shareUtils) { + when (it.text) { + "投诉" -> { + SuggestionActivity.startSuggestionActivity(context, SuggestType.normal, "report", + "问题投诉(" + questionEntity.id + "):") + } + "编辑" -> { + val intent = QuestionEditActivity.getManagerIntent(requireContext(), questionEntity) + startActivityForResult(intent, QuestionsDetailFragment.QUESTIONS_EDIT_REQUEST) + } + + "删除" -> { + DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除问题后,其中的所有回答都将被删除", "取消", "删除", {}, { + mListViewModel.moderatorsHideQuestion() + }) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailViewModel.kt new file mode 100644 index 0000000000..801fed08ba --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailViewModel.kt @@ -0,0 +1,187 @@ +package com.gh.gamecenter.qa.questions.newdetail + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.common.util.NewLogUtils +import com.gh.common.util.ToastUtils +import com.gh.common.util.observableToMain +import com.gh.common.util.singleToMain +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.LoadStatus +import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.entity.Permissions +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.article.detail.CommentItemData +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.Response +import com.lightgame.utils.Utils +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody +import org.json.JSONObject +import retrofit2.HttpException + +class NewQuestionDetailViewModel(application: Application, questionId: String = "", communityId: String = "", val answerId: String = "") : + BaseCommentViewModel(application, articleId = "", communityId = communityId, videoId = "", questionId = questionId) { + var questionRenderedLiveData = MutableLiveData() + var questionPageFinishedLiveData = MutableLiveData() + val moderatorsHideLiveData = MutableLiveData() + val favoriteLiveData = MutableLiveData() + val mFollowLiveData = MutableLiveData() + var questionDetail: QuestionsDetailEntity? = null + val followLiveData = mFollowLiveData + + fun getQuestionDetail() { + mApi.getQuestionsById(questionId) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: QuestionsDetailEntity?) { + super.onResponse(response) + questionDetail = response + topItemData = CommentItemData(questionDetail = response) + commentCount = response?.count?.answer ?: 0 + loadResultLiveData.postValue(LoadResult.SUCCESS) + mergeListData(mListLiveData.value, displayFloor = true) + + NewLogUtils.logForumContentBrowser(questionId, "bbs_question") + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + if (e?.code().toString().startsWith("404")) { + loadResultLiveData.postValue(LoadResult.DELETED) + } else { + loadResultLiveData.postValue(LoadResult.NETWORK_ERROR) + } + } + }) + } + + override fun provideDataObservable(page: Int): Observable>? { + val map = hashMapOf() + if (answerId.isNotEmpty()) { + map["top_comment_id"] = answerId + } + return mApi.getQuestionComment(questionId, currentSortType.value, page, map) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mergeListData(it, displayFloor = true) } + } + + override fun hideCommentSuccess() { + questionDetail?.count?.answer = (questionDetail?.count?.answer ?: 0) - 1 + loadResultLiveData.postValue(LoadResult.SUCCESS) + } + + fun follow() { + followingCommand(true, questionDetail?.user?.id!!) + } + + fun unFollow() { + followingCommand(false, questionDetail?.user?.id!!) + } + + private fun followingCommand(isFollow: Boolean, targetUserId: String) { + val observable = if (isFollow) { + mApi.postFollowing(targetUserId) + } else { + mApi.deleteFollowing(targetUserId) + } + observable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + if (isFollow) { + // 关注成功 + mFollowLiveData.postValue(true) + } else { + // 取消关注成功 + mFollowLiveData.postValue(false) + } + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + Utils.toast(getApplication(), R.string.loading_failed_hint) + } + }) + } + + fun moderatorsHideQuestion() { + val me = questionDetail?.me ?: return + mApi.moderatorsHideQuestion(UserManager.getInstance().userId, questionId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + if (me.moderatorPermissions.hideQuestion == Permissions.REPORTER) { + Utils.toast(getApplication(), "提交成功") + } else { + Utils.toast(getApplication(), "操作成功") + moderatorsHideLiveData.postValue(true) + mLoadStatusLiveData.setValue(LoadStatus.INIT_EMPTY) + } + } + + override fun onFailure(e: HttpException?) { + if (e != null && e.code() == 403) { + val string = e.response().errorBody()?.string() + val errorJson = JSONObject(string) + val errorCode = errorJson.getInt("code") + if (errorCode == 403059) { + Utils.toast(getApplication(), "权限错误,请刷新后重试") + load(LoadType.REFRESH) + return + } + } + Utils.toast(getApplication(), R.string.post_failure_hint) + } + }) + } + + @SuppressLint("CheckResult") + fun postFavoriteQuestion() { + if (questionDetail == null) return + val single = if (questionDetail?.me?.isQuestionFavorite == true) { + mApi.deleteFavoriteQuestion(UserManager.getInstance().userId, questionId) + } else { + mApi.favoriteQuestion(UserManager.getInstance().userId, questionId) + } + single.compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + if (questionDetail?.me?.isQuestionFavorite == true) { + ToastUtils.showToast("取消收藏") + } else { + ToastUtils.showToast("收藏成功") + } + questionDetail?.me?.isQuestionFavorite = questionDetail?.me?.isQuestionFavorite != true + favoriteLiveData.postValue(true) + } + }) + } + + class Factory(private val application: Application, + private val questionId: String = "", + private val communityId: String = "", + private val answerId: String = "") : ViewModelProvider.NewInstanceFactory() { + + override fun create(modelClass: Class): T { + return NewQuestionDetailViewModel( + application = application, + questionId = questionId, + communityId = communityId, + answerId = answerId) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/QuestionDetailContentViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/QuestionDetailContentViewHolder.kt new file mode 100644 index 0000000000..a3b94a56db --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/QuestionDetailContentViewHolder.kt @@ -0,0 +1,171 @@ +package com.gh.gamecenter.qa.questions.newdetail + +import android.annotation.SuppressLint +import android.app.Activity +import android.view.View +import android.webkit.JavascriptInterface +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.runOnUiThread +import com.gh.common.util.* +import com.gh.common.view.RichEditor +import com.gh.gamecenter.ImageViewerActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.ItemArticleDetailContentBinding +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.editor.OnLinkClickListener +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity +import java.util.* + +class QuestionDetailContentViewHolder(var binding: ItemArticleDetailContentBinding, var viewModel: NewQuestionDetailViewModel) + : RecyclerView.ViewHolder(binding.root) { + + private var mEntrance = "" + val questionImgUrlList = ArrayList() + + @SuppressLint("AddJavascriptInterface") + fun bindView(question: QuestionsDetailEntity) { + binding.run { + richEditor.setInputEnabled(false) + richEditor.setPadding(20, 20, 20, 15) + richEditor.addJavascriptInterface(JsInterface(), "imagelistener") + richEditor.addJavascriptInterface(OnLinkClickListener(root.context, mEntrance, "问题详情"), "OnLinkClickListener") + richEditor.setLayoutCallback(object : EmptyCallback { + override fun onCallback() { + viewModel.questionRenderedLiveData.postValue(true) + } + }) + richEditor.setPageFinishedListener { + viewModel.questionPageFinishedLiveData.postValue(true) + } + + followBtn.setOnClickListener { + root.context.ifLogin("问题详情-[关注]用户") { + if (followBtn.text == "关注") { + viewModel.follow() + } else { + DialogUtils.showAlertDialog(root.context, + "取消关注", + "确定要取消关注 ${question.user.name} 吗?", + "确定取消", + "暂不取消", + DialogUtils.ConfirmListener { + viewModel.unFollow() + }, null) + } + } + } + + userNameTv.setOnClickListener { + DirectUtils.directToHomeActivity(root.context, question.user.id, 1, mEntrance, "问题详情") + } + userIconIv.setOnClickListener { + DirectUtils.directToHomeActivity(root.context, question.user.id, 1, mEntrance, "问题详情") + } + + binding.run { + val title = " ${question.title}" + titleTv.text = SpanBuilder(title).image(0, 1, R.drawable.ic_ask_label).build() + userNameTv.text = question.user.name + userIconIv.display(question.user.border, question.user.icon, question.user.auth?.icon) + richEditor.setContentOwner(question.me.isContentOwner) + // 避免列表频繁刷新 + if (richEditor.currentContent != question.description) { + richEditor.setHtml(question.description, true) + } + if (question.time.create == question.time.edit) { + releaseTimeTv.text = String.format("发布于%s", NewsUtils.getFormattedTime(question.time.create)) + } else { + releaseTimeTv.text = String.format("发布于%s 最后编辑于%s", NewsUtils.getFormattedTime(question.time.create), NewsUtils.getFormattedTime(question.time.edit)) + } + richEditor.visibility = View.VISIBLE + question.community.let { entity -> + gameName.text = entity.name + val icon = if (!entity.icon.isNullOrEmpty()) entity.icon else entity.game?.getIcon() + val iconSubscript = if (!entity.iconSubscript.isNullOrEmpty()) entity.iconSubscript else entity.game?.iconSubscript + forumIconView.displayGameIcon(icon, iconSubscript) + forumContainer.setOnClickListener { + DirectUtils.directForumDetail(forumContainer.context, entity.id, "问题详情") + LogUtils.uploadAccessToBbs(entity.id, "文章内所属论坛") + } + } + + followBtn.visibility = View.VISIBLE + if (question.user.id == UserManager.getInstance().userId) { + followBtn.visibility = View.GONE + } else { + followBtn.isEnabled = true + updateFollowBtn(question.me.isFollower) + } + + if (question.tagActivityId.isNotEmpty() && question.tagActivityName.isNotEmpty()) { + activityNameTv.text = question.tagActivityName + activityNameTv.visibility = View.VISIBLE + } + + badgeIv.goneIf(question.user.badge == null) + badgeTv.goneIf(question.user.badge == null) + badgeTv.text = question.user.badge?.name + ImageUtils.display(badgeIv, question.user.badge?.icon) + badgeIv.setOnClickListener { + DialogUtils.showViewBadgeDialog(root.context, question.user.badge) { + DirectUtils.directToBadgeWall(root.context, question.user.id, question.user.name, question.user.icon) + } + } + badgeTv.setOnClickListener { badgeIv.performClick() } + } + } + } + + fun updateFollowBtn(isFollowed: Boolean) { + binding.run { + if (isFollowed) { + if (followBtn.visibility == View.GONE) return + followBtn.text = "已关注" + followBtn.background = null + followBtn.setTextColor(ContextCompat.getColor(root.context, R.color.text_999999)) + } else { + followBtn.setRoundedColorBackground(R.color.text_EEF5FB, 14F) + followBtn.setTextColor(ContextCompat.getColor(root.context, R.color.theme_font)) + followBtn.setText(R.string.concern) + } + } + } + + + inner class JsInterface { + @JavascriptInterface + fun imageClick(url: String) { + when { + url.contains("web_load_dfimg_icon.png") -> { + runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() } + } + url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> { + runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) } + } + else -> { + var current = 0 + var i = 0 + val size = questionImgUrlList.size + while (i < size) { + if (url.contains(questionImgUrlList.get(i))) { + current = i + } + i++ + } + val intent = ImageViewerActivity.getIntent(binding.root.context, questionImgUrlList, current, + mEntrance + "+(问题详情[" + binding.titleTv.text.toString() + "])") + (binding.root.context as Activity).startActivityForResult(intent, ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE) + } + } + } + + @JavascriptInterface + fun imageArr(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) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/recommends/AskQuestionsRecommendsFragment.java b/app/src/main/java/com/gh/gamecenter/qa/recommends/AskQuestionsRecommendsFragment.java index 7c3bbaddd8..0235709149 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/recommends/AskQuestionsRecommendsFragment.java +++ b/app/src/main/java/com/gh/gamecenter/qa/recommends/AskQuestionsRecommendsFragment.java @@ -23,6 +23,7 @@ import com.gh.gamecenter.eventbus.EBTypeChange; import com.gh.gamecenter.eventbus.EBUserFollow; import com.gh.gamecenter.fragment.MainWrapperFragment; import com.gh.gamecenter.manager.UserManager; +import com.gh.gamecenter.qa.BbsType; import com.gh.gamecenter.qa.CommunityFragment; import com.gh.gamecenter.qa.entity.AnswerEntity; import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity; @@ -35,10 +36,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; + import butterknife.BindView; import butterknife.OnClick; -import static com.gh.gamecenter.fragment.MainWrapperFragment.INDEX_ASK; +import static com.gh.gamecenter.fragment.MainWrapperFragment.INDEX_BBS; import static com.gh.gamecenter.personal.PersonalFragment.LOGIN_TAG; import static com.gh.gamecenter.personal.PersonalFragment.LOGOUT_TAG; import static com.gh.gamecenter.qa.CommunityFragment.COMMUNITIES_SELECT_REQUEST; @@ -70,7 +72,7 @@ public class AskQuestionsRecommendsFragment extends ListFragment startActivity(QuestionEditActivity.Companion.getIntent(requireContext())); + mOnLoginListener = () -> startActivity(QuestionEditActivity.Companion.getIntent(requireContext(), BbsType.GAME_BBS)); mParentFragment = exhaustParentFragment(); @@ -85,7 +87,7 @@ public class AskQuestionsRecommendsFragment extends ListFragment(context), ISyncAdapterHandler { @@ -65,7 +66,7 @@ open class RecommendNewestAdapter(context: Context) : ListAdapter( mContext.startActivity(ArticleDetailActivity.getIntent(mContext, UserManager.getInstance().community, answer.id!!, "", getPath())) } else { val questions = answer.questions - mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.id, "", getPath())) + mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, questions.id, "", getPath())) } } diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumTopVideoView.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumTopVideoView.kt new file mode 100644 index 0000000000..828d71b48f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumTopVideoView.kt @@ -0,0 +1,435 @@ +package com.gh.gamecenter.qa.video.detail + +import android.app.Activity +import android.content.Context +import android.os.Handler +import android.util.AttributeSet +import android.view.* +import android.widget.ImageView +import android.widget.SeekBar +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import com.facebook.drawee.view.SimpleDraweeView +import com.gh.common.constant.Constants +import com.gh.common.observer.MuteCallback +import com.gh.common.observer.VolumeObserver +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.video.detail.CustomManager +import com.lightgame.utils.Utils +import com.shuyu.gsyvideoplayer.utils.CommonUtil +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge +import io.reactivex.disposables.Disposable +import kotlinx.android.synthetic.main.layout_game_detail_video_portrait.view.* +import kotlinx.android.synthetic.main.piece_forum_top_video_replay.view.* +import kotlinx.android.synthetic.main.piece_video_control.view.* +import java.util.* + +class ForumTopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : StandardGSYVideoPlayer(context, attrs) { + private var mMuteCallback: MuteCallback + private var mVolumeObserver: VolumeObserver + private var mMuteDisposable: Disposable? = null + private var mIsAutoPlay = false + var thumbImage: SimpleDraweeView = findViewById(R.id.thumbImage) + var mForumVideoEntity: ForumVideoEntity? = null + var viewModel: ForumVideoDetailViewModel? = null + var uuid = UUID.randomUUID().toString() + + init { + post { + gestureDetector = GestureDetector(getContext().applicationContext, object : GestureDetector.SimpleOnGestureListener() { + override fun onSingleTapConfirmed(e: MotionEvent): Boolean { + if (!mChangePosition && !mChangeVolume && !mBrightness) { + onClickUiToggle() + } + return super.onSingleTapConfirmed(e) + } + }) + + if (mIfCurrentIsFullscreen) { + showBackBtn() + } else { + hideBackBtn() + } + + volume.setOnClickListener { toggleMute() } + } + + mMuteCallback = object : MuteCallback { + override fun onMute(isMute: Boolean) { + if (isMute) { + mute() + } else { + unMute() + } + } + } + + mVolumeObserver = VolumeObserver(context, Handler(), mMuteCallback) + + setBackFromFullScreenListener { + clearFullscreenLayout() + } + + errorBtn?.setOnClickListener { + debounceActionWithInterval(errorBtn.id, 1000) { + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext)) { + Utils.toast(context, "网络异常,请检查手机网络状态") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + return@debounceActionWithInterval + } + startPlayLogic(false) + } + } + } + + fun setForumVideoEntity(entity: ForumVideoEntity?) { + this.mForumVideoEntity = entity + } + + //这个必须配置最上面的构造才能生效 + override fun getLayoutId(): Int { + return R.layout.layout_forum_video_detail_videoview_portrait + } + + fun observeVolume(fragment: Fragment?) { + fragment?.context?.applicationContext?.contentResolver?.registerContentObserver( + android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver) + + fragment?.fragmentManager?.registerFragmentLifecycleCallbacks( + object : FragmentManager.FragmentLifecycleCallbacks() { + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { + if (f === fragment) { + fragment.context?.applicationContext?.contentResolver?.unregisterContentObserver(mVolumeObserver) + fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this) + } + } + }, false) + } + + fun startPlayLogic(isAutoPlay: Boolean) { + mIsAutoPlay = isAutoPlay + violenceUpdateMuteStatus() + startPlayLogic() + } + + fun violenceUpdateMuteStatus() { + if (mMuteDisposable != null) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + mMuteDisposable = rxTimer(25) { + if (it >= 400) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + updateMuteStatus() + } + } + + fun disposableTimer() { + if (mMuteDisposable != null && !mMuteDisposable!!.isDisposed) { + mMuteDisposable?.dispose() + mMuteDisposable = null + } + } + + private fun toggleMute() { + if (viewModel?.videoIsMuted == true) { + unMute(true) + } else { + mute(true) + } + } + + fun updateMuteStatus() { + if (viewModel?.videoIsMuted == true) { + mute() + } else { + unMute() + } + } + + private fun mute(isManual: Boolean = false) { + viewModel?.videoIsMuted = true + volume.setImageResource(R.drawable.ic_game_detail_volume_off) + CustomManager.getCustomManager(getKey()).isNeedMute = true + SPUtils.setBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + if (isManual) { + Utils.toast(context, "当前处于静音状态") + uploadVideoStreamingPlaying("点击静音") + } + } + + private fun unMute(isManual: Boolean = false) { + viewModel?.videoIsMuted = false + volume.setImageResource(R.drawable.ic_game_detail_volume_on) + CustomManager.getCustomManager(getKey()).isNeedMute = false + SPUtils.setBoolean(Constants.SP_VIDEO_PLAY_MUTE, false) + if (isManual) { + uploadVideoStreamingPlaying("取消静音") + } + } + + // 重载以减少横竖屏切换的时间 + override fun checkoutState() { + removeCallbacks(mCheckoutTask) + postDelayed(mCheckoutTask, 300) + } + + override fun clearFullscreenLayout() { + super.clearFullscreenLayout() + updateMuteStatus() + hideBackBtn() + } + + fun updateThumb(url: String) { + ImageUtils.display(thumbImage, url) + } + + override fun touchDoubleUp() { + // we do not need double click to play or pause + } + + // 不需要弹弹窗,直接播放 + override fun showWifiDialog() { + startPlayLogic(false) + //val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false) + //if (trafficVideo) { + // 不延迟的话 isCacheFile 可能直接返回 false + postDelayed({ + // 这个库的 NetworkUtils.isWifiConnected 可能会触发空指针,这里换为我们自己的 + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "网络异常,请检查手机网络状态") + } else if (!NetworkUtils.isWifiConnected(mContext) && !gsyVideoManager.isCacheFile) { + Utils.toast(context, "当前为非Wi-Fi环境,请注意流量消耗") + } + }, 100) + // } + } + + override fun getGSYVideoManager(): GSYVideoViewBridge { + CustomManager.getCustomManager(getKey()).initContext(context.applicationContext) + return CustomManager.getCustomManager(getKey()) + } + + fun getKey(): String { + return uuid + } + + override fun onAutoCompletion() { + + //播放完成后判断是否已缓冲完毕,没有完成显示播放错误状态 + if (mBufferPoint != 0 && mBufferPoint != 100 && isShown) { + gsyVideoManager.releaseMediaPlayer() + changeUiToPreparingShow() + postDelayed({ + if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext)) { + Utils.toast(context, "网络错误,视频播放失败") + changeUiToError() + } + }, 10 * 1000) + } + uploadVideoStreamingPlaying("播放完毕") + super.onAutoCompletion() + } + + override fun onStopTrackingTouch(seekBar: SeekBar?) { + super.onStopTrackingTouch(seekBar) + uploadVideoStreamingPlaying("拖动") + } + + override fun isShowNetConfirm(): Boolean { + return (!mOriginUrl.startsWith("file") && !mOriginUrl.startsWith("android.resource") && !CommonUtil.isWifiConnected(context) + && mNeedShowWifiTip) + } + + override fun updateStartImage() { + if (mStartButton is ImageView) { + val imageView = mStartButton as ImageView + when (mCurrentState) { + GSYVideoView.CURRENT_STATE_PLAYING -> imageView.setImageResource(R.drawable.ic_game_detail_pause) + GSYVideoView.CURRENT_STATE_ERROR -> imageView.setImageResource(R.drawable.ic_game_detail_play) + else -> imageView.setImageResource(R.drawable.ic_game_detail_play) + } + } + } + + override fun setStateAndUi(state: Int) { + super.setStateAndUi(state) + + if (state == CURRENT_STATE_AUTO_COMPLETE) { + hideAllWidget() + replayContainer.visibility = View.VISIBLE + mTopContainer.visibility = View.VISIBLE + replayTv.setOnClickListener { + startButton.performClick() + violenceUpdateMuteStatus() + uploadVideoStreamingPlaying("重新播放") + } + shareTv.setOnClickListener { + share() + } + } else { + replayContainer.visibility = View.GONE + } + } + + private fun share() { + mForumVideoEntity?.let { + val shareIcon = it.poster + val shareUrl = if (isPublishEnv()) { + "https://m.ghzs666.com/video/${it.id}" + } else { + "https://resource.ghzs.com/page/video_play/video/video.html?video=${it.id}" + } + ShareUtils.getInstance(context).showShareWindowsCallback(context as Activity, + this, + shareUrl, + shareIcon, + it.title, + it.des, + ShareUtils.ShareEntrance.video, it.id, object : ShareUtils.ShareCallBack { + override fun onSuccess(label: String) { + if ("短信" == label || "复制链接" == label) viewModel?.shareVideoStatistics(it) + } + + override fun onCancel() { + uploadVideoStreamingPlaying("取消分享") + } + }) + } + } + + private fun showBackBtn() { + mTopContainer.background = ContextCompat.getDrawable(context, R.drawable.video_title_bg) + back.visibility = View.VISIBLE + } + + private fun hideBackBtn() { + mTopContainer?.setBackgroundResource(0) + back.visibility = View.GONE + } + + override fun getEnlargeImageRes(): Int { + return R.drawable.ic_game_detail_enter_full_screen + } + + override fun getShrinkImageRes(): Int { + return R.drawable.ic_game_detail_exit_full_screen + } + + /******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/ + + override fun onSurfaceUpdated(surface: Surface) { + super.onSurfaceUpdated(surface) + if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) { + mThumbImageViewLayout.visibility = View.INVISIBLE + uploadVideoStreamingPlaying("开始播放") + } + } + + override fun setViewShowState(view: View?, visibility: Int) { + if (view === mThumbImageViewLayout && visibility != View.VISIBLE) { + return + } + super.setViewShowState(view, visibility) + } + + /********************************各类UI的状态显示*********************************************/ + + override fun changeUiToNormal() { + super.changeUiToNormal() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPreparingShow() { + super.changeUiToPreparingShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPlayingShow() { + super.changeUiToPlayingShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToPauseShow() { + super.changeUiToPauseShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToCompleteShow() { + super.changeUiToCompleteShow() + errorContainer.visibility = View.GONE + } + + override fun changeUiToError() { + super.changeUiToError() + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + override fun netWorkErrorLogic() { + super.netWorkErrorLogic() + Utils.toast(context, "网络错误,视频播放失败") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + //监控播放错误 + override fun onError(what: Int, extra: Int) { + super.onError(what, extra) + Utils.toast(context, "网络错误,视频播放失败") + setViewShowState(mStartButton, View.INVISIBLE) + errorContainer.visibility = View.VISIBLE + } + + override fun releaseVideos() { + uploadVideoStreamingPlaying("结束播放") + CustomManager.releaseAllVideos(getKey()) + } + + override fun onVideoPause() { + super.onVideoPause() + uploadVideoStreamingPlaying("暂停播放") + } + + fun getCurrentPosition(): Long { + return mCurrentPosition + } + + fun uploadVideoStreamingPlaying(action: String) { + /*if (video == null || video?.url.isNullOrEmpty()) return + runOnIoThread { + val isLandscape = mOrientationUtils != null + val videoPlayModel = if (!isLandscape) { + if (mIsAutoPlay) "自动播放" else "点击播放" + } else "全屏播放" + val videoPlayTs = currentPositionWhenPlaying / 1000 + val videoTotalTime = duration / 1000 + val progress = if (videoTotalTime != 0) videoPlayTs.toFloat() / videoTotalTime.toFloat() * 100 else 0f + if (mContentLength == 0.0) { + mContentLength = ExoCacheManager.getContentLength(video!!.url) / 1024.0 / 1024.0 + } + + //https://exoplayer.dev/hello-world.html#a-note-on-threading + runOnUiThread { + LogUtils.uploadTopVideoStreamingPlaying(action, video?.videoId, video?.title, viewModel?.game?.id, viewModel?.game?.name, + videoPlayModel, videoPlayStatus(), mContentLength, videoTotalTime, videoPlayTs, progress.toInt()) + } + }*/ + } + + private fun videoPlayStatus(): String { + return when (mCurrentState) { + CURRENT_STATE_PLAYING, CURRENT_STATE_PREPAREING, CURRENT_STATE_PLAYING_BUFFERING_START -> "play" + GSYVideoView.CURRENT_STATE_PAUSE -> "pause" + else -> "" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailActivity.kt new file mode 100644 index 0000000000..26196d61a8 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailActivity.kt @@ -0,0 +1,47 @@ +package com.gh.gamecenter.qa.video.detail + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import com.gh.base.BaseActivity +import com.gh.common.util.DisplayUtils +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.normal.NormalFragment + +class ForumVideoDetailActivity : BaseActivity() { + var containerFragment: Fragment? = null + + override fun getLayoutId(): Int { + return R.layout.activity_amway + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + DisplayUtils.transparentStatusBar(this) + + containerFragment = supportFragmentManager.findFragmentByTag(ForumVideoDetailFragment::class.java.simpleName) + ?: ForumVideoDetailFragment().with(intent.extras) + // 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移 + supportFragmentManager.beginTransaction().replace(R.id.placeholder, containerFragment!!, ForumVideoDetailFragment::class.java.simpleName).commitAllowingStateLoss() + } + + override fun onBackPressed() { + if (containerFragment is NormalFragment + && (containerFragment as NormalFragment).isAdded + && !(containerFragment as NormalFragment).onBackPressed()) { + super.onBackPressed() + } + } + + companion object { + const val VIDEO_PATCH_REQUEST = 100 + fun getIntent(context: Context, videoId: String): Intent { + val intent = Intent(context, ForumVideoDetailActivity::class.java) + intent.putExtra(EntranceUtils.KEY_VIDEO_ID, videoId) + return intent + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt new file mode 100644 index 0000000000..0ca3297411 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt @@ -0,0 +1,546 @@ +package com.gh.gamecenter.qa.video.detail + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.CheckedTextView +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat +import androidx.core.os.bundleOf +import androidx.core.view.ViewCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.Observer +import com.ethanhua.skeleton.Skeleton +import com.ethanhua.skeleton.ViewSkeletonScreen +import com.gh.base.fragment.BaseFragment_TabLayout +import com.gh.common.constant.Config +import com.gh.common.repository.ReservationRepository +import com.gh.common.simulator.SimulatorGameManager +import com.gh.common.util.* +import com.gh.common.view.FlexLinearLayout +import com.gh.download.DownloadManager +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.SuggestionActivity +import com.gh.gamecenter.databinding.FragmentForumVideoDetailBinding +import com.gh.gamecenter.entity.* +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.gamedetail.GameDetailFragment +import com.gh.gamecenter.gamedetail.dialog.GameTagsDialog +import com.gh.gamecenter.manager.PackagesManager +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.mvvm.Status +import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog +import com.gh.gamecenter.qa.entity.ArticleDetailEntity +import com.gh.gamecenter.qa.entity.QuestionsDetailEntity +import com.gh.gamecenter.qa.video.detail.comment.VideoCommentFragment +import com.gh.gamecenter.qa.video.detail.desc.VideoDescFragment +import com.gh.gamecenter.qa.video.publish.VideoPublishActivity +import com.gh.gamecenter.suggest.SuggestType +import com.gh.gamecenter.tag.TagsActivity +import com.google.android.material.appbar.AppBarLayout +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack +import com.shuyu.gsyvideoplayer.utils.OrientationUtils +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import kotlin.math.abs + +class ForumVideoDetailFragment : BaseFragment_TabLayout() { + private lateinit var mBinding: FragmentForumVideoDetailBinding + private lateinit var mViewModel: ForumVideoDetailViewModel + private var mOrientationUtils: OrientationUtils? = null + private var mSkeleton: ViewSkeletonScreen? = null + private var mCommentCountTv: TextView? = null + private var mMoreMenuItem: MenuItem? = null + private var mForumVideoEntity: ForumVideoEntity? = null + private var mVideoId = "" + + private val dataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + setDownloadButton(mForumVideoEntity?.game) + } + } + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout(): View { + mBinding = FragmentForumVideoDetailBinding.inflate(LayoutInflater.from(requireContext()), null, false) + return mBinding.root + } + + override fun initFragmentList(fragments: MutableList) { + fragments.add(VideoDescFragment().apply { arguments = bundleOf(EntranceUtils.KEY_VIDEO_ID to mVideoId) }) + fragments.add(VideoCommentFragment().apply { arguments = bundleOf(EntranceUtils.KEY_VIDEO_ID to mVideoId) }) + } + + override fun initTabTitleList(tabTitleList: MutableList) { + tabTitleList.add("详情") + tabTitleList.add("评论") + } + + override fun onCreate(savedInstanceState: Bundle?) { + mVideoId = arguments?.getString(EntranceUtils.KEY_VIDEO_ID) ?: "" + super.onCreate(savedInstanceState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mBinding.toolbar.inflateMenu(R.menu.menu_forum_video_detail) + mMoreMenuItem = mBinding.toolbar.menu.findItem(R.id.menu_more) + ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbar) { _, insets -> + (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop + insets.consumeSystemWindowInsets() + } + mViewModel = viewModelProviderFromParent(ForumVideoDetailViewModel.Factory(mVideoId)) + + mSkeleton = Skeleton.bind(mBinding.skeleton).shimmer(false).load(R.layout.fragment_video_detail_skeleton).show() + mBinding.toolbar.setNavigationOnClickListener { requireActivity().finish() } + mOrientationUtils = OrientationUtils(requireActivity(), mBinding.topVideoView) + mOrientationUtils?.isEnable = false + + mBinding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> + val absVerticalOffset = abs(verticalOffset) + val total = appBarLayout.totalScrollRange + if (absVerticalOffset > total / 2) { + updateToolbarStyle(isToolbarWhite = true) + } else { + updateToolbarStyle(isToolbarWhite = false) + } + }) + + mMoreMenuItem?.setOnMenuItemClickListener { + consume { + mForumVideoEntity?.let { + showMoreItemDialog() + } + } + } + mBinding.reuseNoConnection.root.setOnClickListener { + mViewModel.getVideoDetail() + mBinding.reuseLoading.root.visibility = View.VISIBLE + mSkeleton?.show() + } + observeData() + } + + private fun observeData() { + mViewModel.detailLiveData.observeNonNull(this) { + if (it.status == Status.SUCCESS) { + val entity = it.data as ForumVideoEntity + mForumVideoEntity = entity + mCommentCountTv?.text = entity.count.comment.toString() + mBinding.topVideoView.setForumVideoEntity(mForumVideoEntity) + updateVideoParams() + setUpTopVideo(entity.url, entity.poster) + setGameInfo(entity) + mBinding.container.visibility = View.VISIBLE + mBinding.reuseNoneData.root.visibility = View.GONE + mBinding.reuseNoConnection.root.visibility = View.GONE + } else { + mBinding.container.visibility = View.GONE + if (it.exception != null && it.exception.code() == 404) { + mBinding.reuseNoneData.reuseTvNoneData.text = "页面不见了" + mBinding.reuseNoneData.root.visibility = View.VISIBLE + } else { + mBinding.reuseNoConnection.root.visibility = View.VISIBLE + } + } + mBinding.reuseLoading.root.visibility = View.GONE + mSkeleton?.hide() + } + mViewModel.deleteLiveData.observe(viewLifecycleOwner, Observer { + ToastUtils.showToast("删除成功") + requireActivity().finish() + }) + mViewModel.updateDetailLiveData.observeNonNull(this) { + mForumVideoEntity = it + mCommentCountTv?.text = it.count.comment.toString() + setGameInfo(it) + } + mViewModel.highlight.observeNonNull(this) { isHighlighted -> + if (isHighlighted) { + if (mForumVideoEntity!!.me.moderatorPermissions.highlightVideo == Permissions.REPORTER) { + toast("提交成功") + } else { + toast("操作成功") + requireActivity().finish() + } + } else { + toast("权限错误,请刷新后重试") + } + } + } + + private fun setGameInfo(entity: ForumVideoEntity) { + val gameEntity = entity.game + if (gameEntity == null) { + mBinding.gameInfoContainer.visibility = View.GONE + return + } + mBinding.gameIconView.displayGameIcon(gameEntity) + mBinding.gameNameTv.text = gameEntity.name + mBinding.gameScoreTv.text = gameEntity.star.toString() + + mBaseHandler.postDelayed({ + mBinding.gameTagsContainer.setTags(gameEntity.tagStyle) + }, 5) + mBinding.gameTagsContainer.onClickListener = object : FlexLinearLayout.OnItemClickListener { + override fun onMoreClickListener() { + if (!isVisible) return + GameTagsDialog.showGameTagsDialog(requireActivity(), gameEntity.tagStyle, gameEntity.name + ?: "") + } + + override fun onItemClickListener(tag: TagStyleEntity) { + requireContext().startActivity(TagsActivity.getIntent(requireContext(), + tag.name, tag.name, mEntrance, "视频详情")) + } + } + mBinding.gameInfoContainer.setOnClickListener { + GameDetailActivity.startGameDetailActivity(requireContext(), gameEntity, "视频详情") + } + setDownloadButton(gameEntity) + } + + private fun updateToolbarStyle(isToolbarWhite: Boolean) { + if (isToolbarWhite == mViewModel.currentToolbarStatus) return + mViewModel.currentToolbarStatus = isToolbarWhite + mBinding.toolbar.setNavigationIcon(if (isToolbarWhite) R.drawable.ic_bar_back else R.drawable.ic_bar_back_light) + mMoreMenuItem?.setIcon(if (isToolbarWhite) R.drawable.ic_menu_gamedetail_more else R.drawable.ic_menu_gamedetail_more_light) + mBinding.toolbar.setBackgroundColor(ContextCompat.getColor(requireContext(), if (isToolbarWhite) R.color.white else R.color.transparent)) + DisplayUtils.setStatusBarColor(requireActivity(), if (isToolbarWhite) R.color.white else R.color.transparent, true) + DisplayUtils.setLightStatusBar(requireActivity(), isToolbarWhite) + } + + private fun updateVideoParams() { + if (mForumVideoEntity == null) return + val videoInfo = mForumVideoEntity!!.videoInfo + val ratio = if (videoInfo.width < videoInfo.height) { + 3 / 4f //竖屏 + } else { + 180 / 101f //横屏 + } + val updateWidth = DisplayUtils.getScreenWidth() + val updateHeight = updateWidth / ratio + (mBinding.topVideoView.layoutParams as ConstraintLayout.LayoutParams).apply { + width = updateWidth + height = updateHeight.toInt() + mBinding.topVideoView.layoutParams = this + } + } + + private fun setUpTopVideo(url: String, poster: String) { + GSYVideoOptionBuilder() + .setIsTouchWigetFull(false) + .setIsTouchWiget(false) + .setRotateViewAuto(false) + .setShowFullAnimation(false) + .setSeekRatio(1f) + .setUrl(url) + .setCacheWithPlay(true) + .setVideoAllCallBack(object : GSYSampleCallBack() { + override fun onQuitFullscreen(url: String?, vararg objects: Any) { + mOrientationUtils?.backToProtVideo() + mBinding.topVideoView.uploadVideoStreamingPlaying("退出全屏") + } + }) + .build(mBinding.topVideoView) + + mBinding.topVideoView.viewModel = mViewModel + mBinding.topVideoView.updateThumb(poster) + + if (NetworkUtils.isWifiConnected(requireContext())) { + if (mViewModel.isTopVideoPartlyCached(url)) { + mBinding.topVideoView.startPlayLogic(isAutoPlay = true) + } else { + // 未有缓存时,为避免影响页面加载,延迟自动播放视频 + postDelayedRunnable({ + if (activity != null && activity?.isFinishing != true) { + mBinding.topVideoView.startPlayLogic(isAutoPlay = true) + } + }, GameDetailFragment.INITIAL_DELAY) + } + } + + mBinding.topVideoView.fullscreenButton.setOnClickListener { + val horizontalVideoView = mBinding.topVideoView.startWindowFullscreen(requireContext(), true, true) as? ForumTopVideoView + if (horizontalVideoView == null) { + toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤") + return@setOnClickListener + } + mOrientationUtils?.resolveByClick() + horizontalVideoView.uuid = mBinding.topVideoView.uuid + horizontalVideoView.viewModel = mViewModel + horizontalVideoView.updateThumb(poster) + horizontalVideoView.violenceUpdateMuteStatus() + mBinding.topVideoView.uploadVideoStreamingPlaying("开始播放") + mBinding.topVideoView.uploadVideoStreamingPlaying("点击全屏") + } + + mBinding.topVideoView.observeVolume(this) + } + + override fun provideTabView(position: Int, title: String?): View { + val view = LayoutInflater.from(requireContext()).inflate(R.layout.tab_item_forum_video_detail, null) + val tabTitle = view.findViewById(R.id.tab_title) + val tabCount = view.findViewById(R.id.tab_count) + if (tabTitle is CheckedTextView) { + tabTitle.text = title + } + if (position == 1) { + tabCount.visibility = View.VISIBLE + tabCount.text = "0" + mCommentCountTv = tabCount + } + return view + } + + private fun setDownloadButton(gameEntity: GameEntity?) { + if (gameEntity == null) return + DownloadItemUtils.setOnClickListener(requireContext(), mBinding.downloadBtn, + gameEntity, 0, null, + mEntrance, "视频详情", null, null) + + // 显示预约 + if (gameEntity.isReservable) { + if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.id)) { + mBinding.downloadBtn.text = "预约" + mBinding.downloadBtn.setTextColor(Color.WHITE) + mBinding.downloadBtn.setBackgroundResource(R.drawable.button_reserve) + } else { + mBinding.downloadBtn.text = "已预约" + mBinding.downloadBtn.setTextColor(Color.WHITE) + mBinding.downloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_dn) + } + return + } + mBinding.downloadBtn.goneIf(!Config.isShowDownload(gameEntity.id) || "光环助手" == gameEntity.name) + if (gameEntity.getApk().size == 0 || gameEntity.downloadOffStatus != null) { + val h5LinkEntity = gameEntity.h5Link + val offStatus = gameEntity.downloadOffStatus + if (h5LinkEntity != null) run { + mBinding.downloadBtn.text = if ("play" == h5LinkEntity.type) { + "开始玩" + } else { + "查看" + } + mBinding.downloadBtn.setTextColor(Color.WHITE) + mBinding.downloadBtn.setBackgroundResource(R.drawable.textview_concern_red_up_round) + mBinding.downloadBtn.isClickable = true + } else { + when (offStatus) { + "dialog" -> { + mBinding.downloadBtn.text = "查看" + mBinding.downloadBtn.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) + } + "updating" -> { + mBinding.downloadBtn.text = "更新中" + mBinding.downloadBtn.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) + mBinding.downloadBtn.setBackgroundResource(R.drawable.download_button_updating_style) + } + else -> { + mBinding.downloadBtn.text = "暂无" + mBinding.downloadBtn.setTextColor(ContextCompat.getColor(requireContext(), R.color.button_gray)) + mBinding.downloadBtn.setBackgroundResource(R.drawable.news_detail_comment) + } + } + mBinding.downloadBtn.isClickable = false + } + } else if (gameEntity.getApk().size == 1) { + setDownloadBtnStatus(requireContext(), gameEntity, mBinding.downloadBtn, PluginLocation.only_game) + val downloadEntity = DownloadManager.getInstance(requireContext()).getDownloadEntityByUrl(gameEntity.getApk()[0].url) + if (downloadEntity != null) { + if (downloadEntity.status == DownloadStatus.done) { + if (SimulatorGameManager.isSimulatorGame(gameEntity)) { + val isInstalled = PackageUtils.isInstalled(context, gameEntity.simulator?.apk?.packageName) + if (isInstalled) { + mBinding.downloadBtn.setText(R.string.launch) + } else { + mBinding.downloadBtn.setText(R.string.install) + } + mBinding.downloadBtn.setBackgroundResource(R.drawable.textview_concern_red_up_round) + } else { + mBinding.downloadBtn.setText(R.string.install) + if (downloadEntity.isPluggable && PackagesManager.isInstalled(downloadEntity.packageName)) { + mBinding.downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style) + } else { + mBinding.downloadBtn.setBackgroundResource(R.drawable.textview_concern_red_up_round) + } + } + mBinding.downloadBtn.setTextColor(Color.WHITE) + } else { + if (downloadEntity.status == DownloadStatus.waiting) { + mBinding.downloadBtn.setText(R.string.waiting) + } else { + mBinding.downloadBtn.setText(R.string.downloading) + } + mBinding.downloadBtn.setBackgroundResource(R.drawable.textview_concern_red_up_round) + mBinding.downloadBtn.setTextColor(ContextCompat.getColorStateList(requireContext(), R.color.white)) + } + } + } else { + setDownloadBtnStatus(requireContext(), gameEntity, mBinding.downloadBtn, PluginLocation.only_game) + } + } + + private fun setDownloadBtnStatus(context: Context, gameEntity: GameEntity, downloadBtn: TextView, pluginLocation: PluginLocation) { + val status = GameUtils.getDownloadBtnText(context, gameEntity, pluginLocation) + downloadBtn.setTextColor(Color.WHITE) + downloadBtn.text = status + if ("插件化" == status) { + downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style) + } else if ("打开" == status || "启动" == status) { + downloadBtn.setBackgroundResource(R.drawable.detail_download_open_style) + downloadBtn.setTextColor(ContextCompat.getColor(context, R.color.theme_font)) + } else { + downloadBtn.setBackgroundResource(R.drawable.textview_concern_red_up_round) + } + } + + private fun showMoreItemDialog() { + if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) || mForumVideoEntity != null) { + val entities = ArrayList() + if (mForumVideoEntity?.user?.id == UserManager.getInstance().userId) { + entities.add(MenuItemEntity("修改", R.drawable.icon_more_panel_edit)) + } + if (mForumVideoEntity?.user?.id != UserManager.getInstance().userId) { + entities.add(MenuItemEntity("投诉", R.drawable.icon_gamedetail_copyright)) + } + if (mForumVideoEntity?.me!!.isModerator) { + val isEnable = mForumVideoEntity?.isHighlighted == true + entities.add(MenuItemEntity("加精", if (isEnable) + R.drawable.icon_more_panel_essence_unenable else R.drawable.icon_more_panel_essence, isEnable = !isEnable)) + } + if (mForumVideoEntity?.me!!.isModerator || mForumVideoEntity?.user?.id == UserManager.getInstance().userId) { + entities.add(MenuItemEntity("删除", R.drawable.icon_more_panel_delete)) + } + val shareUrl = if (isPublishEnv()) { + requireContext().getString(R.string.share_community_article_url, mForumVideoEntity?.bbsId, mForumVideoEntity?.id) + } else { + requireContext().getString(R.string.share_community_article_url_dev, mForumVideoEntity?.bbsId, mForumVideoEntity?.id) + } + + val shareIcon: String = if (mForumVideoEntity?.poster?.isNotEmpty() == true) { + mForumVideoEntity?.poster ?: "" + } else { + requireContext().getString(R.string.share_ghzs_logo) + } + val shareUtils = ShareUtils.getInstance(requireContext()) + shareUtils.shareParamsDetail(requireActivity(), + shareUrl, + shareIcon, + mForumVideoEntity?.title ?: "", + mForumVideoEntity?.des ?: "", + ShareUtils.ShareEntrance.communityArticle, + mForumVideoEntity?.id, null) + MoreFunctionPanelDialog.showMoreDialog(requireActivity() as AppCompatActivity, entities, mForumVideoEntity?.title + ?: "", shareUtils) { + when (it.text) { + "修改" -> { + startActivityForResult(VideoPublishActivity.getIntent(requireContext(), mForumVideoEntity!!, mEntrance, "视频详情"), ForumVideoDetailActivity.VIDEO_PATCH_REQUEST) + } + "投诉" -> { + SuggestionActivity.startSuggestionActivity(requireContext(), SuggestType.normal, "report", + "视频投诉(" + mForumVideoEntity?.id + "):") + } + "加精" -> { + addEssenceForum() + } + "删除" -> { + DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除视频后,其中的所有评论及回复都将被删除", "取消", "删除", {}, { + mViewModel.deleteVideo(mForumVideoEntity?.id ?: "") + }) + } + } + } + } + } + + private fun addEssenceForum() { + if (mForumVideoEntity == null) return + var highlightDialogHintContent = "" + val permissions = mForumVideoEntity?.me?.moderatorPermissions ?: Permissions() + if (permissions.highlightVideo > Permissions.GUEST) { + highlightDialogHintContent = if (permissions.highlightVideo == Permissions.REPORTER) { + "你的操作将提交给小编审核,确定提交吗?" + } else { + "你的操作将立即生效,确定提交吗?(你的管理权限为:高级)" + } + } + if (mForumVideoEntity?.isHighlighted == true) { + toast("帖子已经加精") + } else { + DialogUtils.showAlertDialog(requireContext(), "加精视频", highlightDialogHintContent, + "确定", "取消", + { + mViewModel.doHighlightThisVideo(mForumVideoEntity?.bbsId ?: "", mVideoId) + }, null) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (data == null || resultCode != Activity.RESULT_OK) return + if (requestCode == ForumVideoDetailActivity.VIDEO_PATCH_REQUEST) { + val entity = data.getParcelableExtra(ForumVideoEntity::class.java.simpleName) + mForumVideoEntity?.let { + it.title = entity?.title ?: "" + it.des = entity?.des ?: "" + mViewModel.updateDetailLiveData.postValue(it) + } + } + } + + //下载被删除事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBDownloadStatus) { + if ("delete" == status.status) { + setDownloadButton(mForumVideoEntity?.game) + } + } + + //安装、卸载事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(busFour: EBPackage) { + setDownloadButton(mForumVideoEntity?.game) + } + + override fun onResume() { + super.onResume() + mBinding.topVideoView.onVideoResume() + DownloadManager.getInstance(requireContext()).addObserver(dataWatcher) + } + + override fun onPause() { + super.onPause() + mBinding.topVideoView.onVideoPause() + DownloadManager.getInstance(requireContext()).removeObserver(dataWatcher) + } + + override fun onDestroy() { + super.onDestroy() + mBinding.topVideoView.release() + mBinding.topVideoView.disposableTimer() + } + + override fun onBackPressed(): Boolean { + if (SyncDataBetweenPageHelper.setResultAndFinish(requireActivity(), mForumVideoEntity)) { + return true + } + return super.onBackPressed() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailViewModel.kt new file mode 100644 index 0000000000..10a372f759 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailViewModel.kt @@ -0,0 +1,127 @@ +package com.gh.gamecenter.qa.video.detail + +import android.annotation.SuppressLint +import android.app.Application +import android.net.Uri +import android.text.TextUtils +import androidx.lifecycle.* +import com.gh.common.constant.Constants +import com.gh.common.util.ErrorHelper +import com.gh.common.util.NewLogUtils +import com.gh.common.util.SPUtils +import com.gh.common.util.observableToMain +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.mvvm.Resource +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.google.android.exoplayer2.upstream.cache.CacheUtil +import com.google.gson.JsonObject +import com.halo.assistant.HaloApp +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody +import retrofit2.HttpException +import tv.danmaku.ijk.media.exo2.ExoSourceManager + +class ForumVideoDetailViewModel(application: Application, val videoId: String) : AndroidViewModel(application) { + private val mApi = RetrofitManager.getInstance(getApplication()).api + var videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + val detailLiveData = MediatorLiveData>() + val updateDetailLiveData = MediatorLiveData() + var needToUpdateShareCount = MutableLiveData() + var deleteLiveData = MutableLiveData() + val highlight = MutableLiveData() + var currentToolbarStatus = true + + init { + getVideoDetail() + } + + fun getVideoDetail() { + mApi.getBbsVideoDetail(videoId) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ForumVideoEntity?) { + super.onResponse(response) + if (response != null) { + detailLiveData.postValue(Resource.success(response)) + + NewLogUtils.logForumContentBrowser(videoId, "bbs_video") + } + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + detailLiveData.postValue(Resource.error(e)) + } + }) + } + + @SuppressLint("CheckResult") + fun shareVideoStatistics(videoEntity: ForumVideoEntity?) { + if (videoEntity == null) return + mApi.shareVideoStatistics(videoEntity.id) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: JsonObject) { + val msg = data.get("msg").asString + if ("success" == msg) { + videoEntity.let { + it.share++ + } + needToUpdateShareCount.postValue(videoEntity.share) + } + } + }) + + } + + fun deleteVideo(videoId: String) { + mApi.deleteVideo(videoId) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + deleteLiveData.postValue(true) + } + + override fun onFailure(e: HttpException?) { + val string = e?.response()?.errorBody()?.string() ?: "" + ErrorHelper.handleError(getApplication(), string) + } + }) + } + + + fun doHighlightThisVideo(bbsId: String, videoId: String) { + mApi.highlightCommunityVideo(bbsId, videoId) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + highlight.postValue(true) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + highlight.postValue(false) + } + }) + } + + fun isTopVideoPartlyCached(topVideoUrl: String): Boolean { + val cache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null) + val key = CacheUtil.generateKey(Uri.parse(topVideoUrl)) + return if (!TextUtils.isEmpty(key)) { + val cachedSpans = cache.getCachedSpans(key) + cachedSpans.size != 0 + } else { + false + } + } + + class Factory(private val videoId: String) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return ForumVideoDetailViewModel(HaloApp.getInstance().application, videoId) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentAdapter.kt new file mode 100644 index 0000000000..c620d7584e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentAdapter.kt @@ -0,0 +1,23 @@ +package com.gh.gamecenter.qa.video.detail.comment + +import android.content.Context +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.R +import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter + +class VideoCommentAdapter(val context: Context, val mViewModel: VideoCommentViewModel, val entrance: String) + : BaseCommentAdapter(context, mViewModel, AdapterType.COMMENT, entrance) { + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is CommentErrorViewHolder) { + val container = holder.itemView.findViewById(R.id.container) + (container.layoutParams as RecyclerView.LayoutParams).apply { + height = RecyclerView.LayoutParams.MATCH_PARENT + container.layoutParams = this + } + } else { + super.onBindViewHolder(holder, position) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentFragment.kt new file mode 100644 index 0000000000..204dcd0ce3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentFragment.kt @@ -0,0 +1,159 @@ +package com.gh.gamecenter.qa.video.detail.comment + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.* +import com.gh.common.view.CustomDividerItemDecoration +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.LazyListFragment +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.databinding.FragmentVideoCommentListBinding +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.mvvm.Status +import com.gh.gamecenter.qa.article.detail.CommentItemData +import com.gh.gamecenter.qa.comment.CommentActivity +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailViewModel + +class VideoCommentFragment : LazyListFragment() { + private lateinit var mBinding: FragmentVideoCommentListBinding + private lateinit var mVideoDetailViewModel: ForumVideoDetailViewModel + private var mAdapter: VideoCommentAdapter? = null + private var mVideoId = "" + + override fun provideListAdapter(): ListAdapter<*> { + return mAdapter ?: VideoCommentAdapter(requireContext(), mListViewModel, mEntrance).apply { + mAdapter = this + } + } + + override fun getRealLayoutId(): Int = R.layout.fragment_video_comment_list + + override fun getItemDecoration(): RecyclerView.ItemDecoration { + val insetDivider = ContextCompat.getDrawable(requireContext(), R.drawable.divider_item_line_space_16) + val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheLastItem = true) + itemDecoration.setDrawable(insetDivider!!) + return itemDecoration + } + + override fun provideListViewModel(): VideoCommentViewModel { + return viewModelProvider(VideoCommentViewModel.Factory(mVideoId)) + } + + override fun onRealLayoutInflated(inflatedView: View) { + mBinding = FragmentVideoCommentListBinding.bind(inflatedView) + } + + override fun onCreate(savedInstanceState: Bundle?) { + mVideoId = arguments?.getString(EntranceUtils.KEY_VIDEO_ID) ?: "" + super.onCreate(savedInstanceState) + } + + override fun onFragmentFirstVisible() { + mVideoDetailViewModel = viewModelProviderFromParent() + super.onFragmentFirstVisible() + mListRefresh?.isEnabled = false + mBinding.replyTv.setDebouncedClickListener { + startCommentActivity() + } + mBinding.filterOldestTv.setOnClickListener { + mListViewModel.changeSort(BaseCommentViewModel.SortType.OLDEST) + updateSortType() + } + mBinding.filterLatestTv.setOnClickListener { + mListViewModel.changeSort(BaseCommentViewModel.SortType.LATEST) + updateSortType() + } + observeData() + } + + private fun observeData() { + mVideoDetailViewModel.detailLiveData.observeNonNull(this) { + if (it.status == Status.SUCCESS) { + mListViewModel.videoDetail = it.data as ForumVideoEntity + mBinding.allCommentCountTv.text = mListViewModel.videoDetail?.count?.comment.toString() + onRefresh() + } + } + mVideoDetailViewModel.updateDetailLiveData.observeNonNull(this) { + mListViewModel.videoDetail = it + mBinding.allCommentCountTv.text = it.count.comment.toString() + } + mListViewModel.deleteCommentLiveData.observeNonNull(this) { + mBinding.allCommentCountTv.text = mListViewModel.videoDetail?.count?.comment.toString() + mVideoDetailViewModel.updateDetailLiveData.postValue(mListViewModel.videoDetail) + } + } + + private fun updateSortType() { + mBinding.run { + val isLatestSelected = mListViewModel.currentSortType == BaseCommentViewModel.SortType.LATEST + val selectedTextView = if (isLatestSelected) filterLatestTv else filterOldestTv + val unselectedTextView = if (!isLatestSelected) filterLatestTv else filterOldestTv + + selectedTextView.setTextColor(R.color.theme_font.toColor()) + unselectedTextView.setTextColor(R.color.text_999999.toColor()) + } + } + + private fun startCommentActivity() { + mListViewModel.videoDetail?.let { + val intent = CommentActivity.getVideoCommentIntent( + requireContext(), + it.id, + it.count.comment, + it.user.id == UserManager.getInstance().userId, + true, true, false + ) + startActivityForResult(intent, CommentActivity.REQUEST_CODE) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (data == null || resultCode != Activity.RESULT_OK) return + if (requestCode == CommentActivity.REQUEST_CODE) { + mListViewModel.videoDetail?.let { + it.count.comment = it.count.comment + 1 + mBinding.allCommentCountTv.text = it.count.comment.toString() + mVideoDetailViewModel.updateDetailLiveData.postValue(it) + mListViewModel.load(LoadType.REFRESH) + } + } + } + + override fun onLoadRefresh() { + showSkeleton(true) + } + + override fun onLoadEmpty() { + showSkeleton(false) + if (mReuseNoConn != null) mReuseNoConn!!.visibility = View.GONE + if (mReuseNoData != null) mReuseNoData!!.visibility = View.GONE + if (mListLoading != null) mListLoading!!.visibility = View.GONE + + mListRv.visibility = View.VISIBLE + hideRefreshingLayout() + } + + override fun onLoadDone() { + mReuseNoConn?.visibility = View.GONE + mReuseNoData?.visibility = View.GONE + mListLoading?.visibility = View.GONE + + mListRv.visibility = View.VISIBLE + hideRefreshingLayout() + + mListRv.postDelayed({ + if (provideListAdapter().itemCount < theNumberNeededToFillAScreen()) { + autoLoadMore() + } + }, autoLoadMoreDelay.toLong()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentViewModel.kt new file mode 100644 index 0000000000..7ab2d087e6 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentViewModel.kt @@ -0,0 +1,50 @@ +package com.gh.gamecenter.qa.video.detail.comment + +import android.app.Application +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.gamecenter.baselist.LoadStatus +import com.gh.gamecenter.entity.CommentEntity +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel +import com.gh.gamecenter.qa.article.detail.CommentItemData +import com.halo.assistant.HaloApp +import io.reactivex.Observable + +class VideoCommentViewModel(application: Application, videoId: String) : BaseCommentViewModel(application, "", videoId, "", "") { + var videoDetail: ForumVideoEntity? = null + val deleteCommentLiveData = MutableLiveData() + + override fun provideDataObservable(page: Int): Observable>? { + return mApi.getVideoCommentList(videoId, page, mapOf("sort" to currentSortType.value)) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { list -> + val itemDataList = arrayListOf() + if (list.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_EMPTY) { + itemDataList.add(CommentItemData(errorEmpty = true)) + } else if (list.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_FAILED) { + itemDataList.add(CommentItemData(errorConnection = true)) + } else { + list.forEach { + itemDataList.add(CommentItemData(commentNormal = it)) + } + itemDataList.add(CommentItemData(footer = true)) + } + mResultLiveData.postValue(itemDataList) + } + } + + override fun hideCommentSuccess() { + videoDetail?.count?.comment = (videoDetail?.count?.comment ?: 0) - 1 + deleteCommentLiveData.postValue(true) + } + + class Factory(private val videoId: String) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return VideoCommentViewModel(HaloApp.getInstance().application, videoId) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescAdapter.kt new file mode 100644 index 0000000000..f11420e4f4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescAdapter.kt @@ -0,0 +1,100 @@ +package com.gh.gamecenter.qa.video.detail.desc + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.constant.ItemViewType +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.databinding.ItemForumVideoBinding +import com.gh.gamecenter.databinding.ItemVideoDescTopBinding +import com.gh.gamecenter.entity.VideoDescItemEntity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailViewModel + +class VideoDescAdapter(context: Context, val mVideoDetailViewModel: ForumVideoDetailViewModel, val mViewModel: VideoDescViewModel) : + ListAdapter(context) { + private var mContentIsExpand = false + private var expandHeight = 0 + private var shrinkHeight = 0 + + override fun areItemsTheSame(oldItem: VideoDescItemEntity?, newItem: VideoDescItemEntity?): Boolean { + return oldItem == newItem || oldItem?.recommendVideo?.id == newItem?.recommendVideo?.id + } + + override fun areContentsTheSame(oldItem: VideoDescItemEntity?, newItem: VideoDescItemEntity?): Boolean { + return oldItem?.recommendVideo?.count?.vote == newItem?.recommendVideo?.count?.vote + && oldItem?.recommendVideo?.count?.comment == newItem?.recommendVideo?.count?.comment + } + + override fun getItemViewType(position: Int): Int { + return if (position == itemCount - 1) { + ItemViewType.ITEM_FOOTER + } else { + val entity = mEntityList[position] + when { + entity.topVideoInfo != null -> { + VIDEO_TOP_DETAIL + } + entity.recommendVideo != null -> { + VIDEO_RECOMMEND + } + else -> { + -1 + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ItemViewType.ITEM_FOOTER -> { + FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) + } + VIDEO_TOP_DETAIL -> { + VideoDescTopViewHolder(ItemVideoDescTopBinding.inflate(LayoutInflater.from(mContext), parent, false), mContentIsExpand, shrinkHeight, expandHeight, mVideoDetailViewModel, mViewModel) + } + VIDEO_RECOMMEND -> { + VideoItemViewHolder(ItemForumVideoBinding.inflate(LayoutInflater.from(mContext), parent, false)) + } + else -> { + throw NullPointerException() + } + } + } + + override fun getItemCount(): Int { + return if (mEntityList.isNotEmpty()) mEntityList.size + FOOTER_ITEM_COUNT else 0 + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is VideoDescTopViewHolder -> { + val videoDescItemEntity = mEntityList[position] + holder.bindData(videoDescItemEntity.topVideoInfo!!) + } + is VideoItemViewHolder -> { + val entity = mEntityList[position] + val recommendVideo = entity.recommendVideo + holder.binding.video = recommendVideo + holder.binding.root.setOnClickListener { + recommendVideo?.let { + mContext.startActivity(ForumVideoDetailActivity.getIntent(mContext, it.id)) + } + } + } + is FooterViewHolder -> { + holder.initItemPadding() + holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver) + } + } + } + + companion object { + const val VIDEO_TOP_DETAIL = 10 + const val VIDEO_RECOMMEND = 11 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescFragment.kt new file mode 100644 index 0000000000..bd447f759c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescFragment.kt @@ -0,0 +1,61 @@ +package com.gh.gamecenter.qa.video.detail.desc + +import android.view.View +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.observeNonNull +import com.gh.common.util.viewModelProviderFromParent +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.LazyListFragment +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.entity.VideoDescItemEntity +import com.gh.gamecenter.mvvm.Status +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailViewModel + +class VideoDescFragment : LazyListFragment() { + + private var mAdapter: VideoDescAdapter? = null + private lateinit var mVideoDetailViewModel: ForumVideoDetailViewModel + + override fun getRealLayoutId(): Int = R.layout.fragment_list_base + + override fun provideListAdapter(): ListAdapter<*> { + return mAdapter ?: VideoDescAdapter(requireContext(), mVideoDetailViewModel, mListViewModel) + .apply { mAdapter = this } + } + + override fun getItemDecoration(): RecyclerView.ItemDecoration? = null + + override fun onFragmentFirstVisible() { + mVideoDetailViewModel = viewModelProviderFromParent() + super.onFragmentFirstVisible() + mListRefresh?.isEnabled = false + observeData() + } + + private fun observeData() { + mVideoDetailViewModel.detailLiveData.observeNonNull(this) { + if (it.status == Status.SUCCESS) { + mListViewModel.topVideoDetail = it.data as ForumVideoEntity + mListViewModel.mergeListData(mListViewModel.listLiveData.value) + mListLoading?.visibility = View.GONE + onRefresh() + } + } + + mVideoDetailViewModel.updateDetailLiveData.observe(this, Observer { + mAdapter?.notifyItemChanged(0) + }) + + mVideoDetailViewModel.needToUpdateShareCount.observe(this, Observer { + mAdapter?.notifyItemChanged(0) + }) + + mListViewModel.handleSuccessLiveData.observe(this, Observer { + mAdapter?.notifyItemChanged(0) + }) + } + + override fun isAutomaticLoad(): Boolean = false +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescTopViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescTopViewHolder.kt new file mode 100644 index 0000000000..20729d006f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescTopViewHolder.kt @@ -0,0 +1,159 @@ +package com.gh.gamecenter.qa.video.detail.desc + +import android.animation.ValueAnimator +import android.app.Activity +import android.text.Layout +import android.view.View +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.animation.doOnEnd +import androidx.core.animation.doOnStart +import androidx.core.view.marginTop +import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.* +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.ItemVideoDescTopBinding +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailViewModel + +class VideoDescTopViewHolder(val binding: ItemVideoDescTopBinding, var mIsExpand: Boolean, var mShrinkHeight: Int, var mExpandHeight: Int, val mVideoDetailViewModel: ForumVideoDetailViewModel, val mViewModel: VideoDescViewModel) : BaseRecyclerViewHolder(binding.root) { + private var mIsAnimationFinish = true + fun bindData(entity: ForumVideoEntity) { + ImageUtils.display(binding.userAvatar, entity.user.icon) + binding.userNameTv.text = entity.user.name + binding.fansAndTimeTv.text = "${entity.user.count.fans}粉丝 · ${CommentUtils.getCommentTime(entity.time.upload)}" + binding.titleTv.text = entity.title + binding.desTv.text = entity.des + binding.desTv.goneIf(entity.des.isEmpty()) + initAnimation() + + binding.likeCountTv.text = entity.count.vote.toString() + binding.collectCountTv.text = entity.count.favorite.toString() + binding.shareTv.text = entity.share.toString() + binding.activityNameTv.goneIf(entity.tagActivityName.isEmpty()) + binding.activityNameTv.text = entity.tagActivityName + binding.likeCountTv.setTextColor(if (entity.me.isVoted) R.color.theme_font.toColor() else R.color.text_999999.toColor()) + binding.likeIv.setImageDrawable(if (entity.me.isVoted) R.drawable.ic_forum_video_detail_liked.toDrawable() else R.drawable.ic_forum_video_detail_like.toDrawable()) + binding.collectCountTv.setTextColor(if (entity.me.isVideoFavorite) R.color.theme_font.toColor() else R.color.text_999999.toColor()) + binding.collectIv.setImageDrawable(if (entity.me.isVideoFavorite) R.drawable.ic_forum_video_detail_collected.toDrawable() else R.drawable.ic_forum_video_detail_collect.toDrawable()) + binding.concernBtn.background = if (entity.me.isFollower) R.drawable.bg_shape_f5_radius_999.toDrawable() else R.drawable.textview_concern_red_up_round.toDrawable() + binding.concernBtn.setTextColor(if (entity.me.isFollower) R.color.text_999999.toColor() else R.color.white.toColor()) + binding.concernBtn.text = if (entity.user.id != UserManager.getInstance().userId) { + if (entity.me.isFollower) "已关注" else "关注" + } else { + binding.concernBtn.background = R.drawable.bg_shape_f5_radius_999.toDrawable() + binding.concernBtn.setTextColor(R.color.text_999999.toColor()) + "自己" + } + + binding.likeContainer.setOnClickListener { + debounceActionWithInterval(it.id) { + mViewModel.postVote() + } + } + binding.collectContainer.setOnClickListener { + debounceActionWithInterval(it.id) { + mViewModel.postFavorite() + } + } + binding.concernBtn.setOnClickListener { + if (entity.user.id != UserManager.getInstance().userId) { + debounceActionWithInterval(it.id) { + mViewModel.followCommand() + } + } + } + binding.shareContainer.setOnClickListener { + share(entity) + } + } + + private fun initAnimation() { + binding.root.post { + mShrinkHeight = if (mShrinkHeight == 0) { + getTextViewHeight(binding.titleTv, 1) + } else mShrinkHeight + + mExpandHeight = if (mExpandHeight == 0) { + getTextViewHeight(binding.titleTv) + getTextViewHeight(binding.desTv) + + if (binding.desTv.visibility == View.VISIBLE) binding.desTv.marginTop else 0 + } else mExpandHeight + + if (mIsExpand) { + binding.titleTv.maxLines = Int.MAX_VALUE + updateTitleContainerHeight(mExpandHeight) + } else { + binding.titleTv.maxLines = 1 + updateTitleContainerHeight(mShrinkHeight) + } + } + binding.expandMoreIv.setOnClickListener { + val animator = if (mIsExpand) { + ValueAnimator.ofInt(mExpandHeight, mShrinkHeight) + } else { + ValueAnimator.ofInt(mShrinkHeight, mExpandHeight) + } + animator.duration = 300 + animator.addUpdateListener { + val value = it.animatedValue as Int + updateTitleContainerHeight(value) + } + animator.doOnStart { + if (!mIsExpand) { + binding.titleTv.maxLines = Int.MAX_VALUE + } + mIsAnimationFinish = false + } + animator.doOnEnd { + if (mIsExpand) { + binding.titleTv.maxLines = 1 + } + mIsExpand = !mIsExpand + binding.expandMoreIv.rotation = if (mIsExpand) 180f else 0f + mIsAnimationFinish = true + } + if (mIsAnimationFinish) { + animator.start() + } + } + } + + private fun share(entity: ForumVideoEntity) { + val shareIcon = entity.poster + val shareUrl = if (isPublishEnv()) { + "https://m.ghzs666.com/video/${entity.id}" + } else { + "https://resource.ghzs.com/page/video_play/video/video.html?video=${entity.id}" + } + ShareUtils.getInstance(binding.root.context).showShareWindowsCallback(binding.root.context as Activity, + (binding.root.context as Activity).window.decorView, + shareUrl, + shareIcon, + entity.title, + entity.des, + ShareUtils.ShareEntrance.video, entity.id, object : ShareUtils.ShareCallBack { + override fun onSuccess(label: String) { + if ("短信" == label || "复制链接" == label) mVideoDetailViewModel.shareVideoStatistics(entity) + } + + override fun onCancel() { + } + }) + } + + private fun updateTitleContainerHeight(newHeight: Int) { + (binding.contentContainer.layoutParams as ConstraintLayout.LayoutParams).apply { + height = newHeight + binding.contentContainer.layoutParams = this + } + } + + private fun getTextViewHeight(view: TextView, lineCount: Int = 0): Int { + if (view.visibility == View.GONE) return 0 + val layout: Layout = view.layout + val desired: Int = layout.getLineTop(if (lineCount > 0) lineCount else view.lineCount) + val padding = view.compoundPaddingTop + view.compoundPaddingBottom + return desired + padding + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescViewModel.kt new file mode 100644 index 0000000000..48940b7cd3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoDescViewModel.kt @@ -0,0 +1,199 @@ +package com.gh.gamecenter.qa.video.detail.desc + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.common.util.* +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.entity.VideoDescItemEntity +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.lightgame.utils.Utils +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody +import retrofit2.HttpException + +class VideoDescViewModel(application: Application) : ListViewModel(application) { + var topVideoDetail: ForumVideoEntity? = null + var handleSuccessLiveData = MutableLiveData() + + override fun provideDataObservable(page: Int): Observable>? { + return RetrofitManager.getInstance(getApplication()).api.getRecommendVideo(topVideoDetail?.id, page) + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mergeListData(it) } + } + + fun mergeListData(videoList: List?) { + val mergedList = arrayListOf().apply { + if (topVideoDetail != null) { + if (mResultLiveData.value?.firstOrNull() != null) { + add(mResultLiveData.value!![0]) + } else { + add(VideoDescItemEntity(topVideoInfo = topVideoDetail)) + } + } + videoList?.forEach { + add(VideoDescItemEntity(recommendVideo = it)) + } + } + mResultLiveData.postValue(mergedList) + } + + fun postVote() { + if (topVideoDetail == null) return + if (!UserManager.getInstance().isLoggedIn) { + CheckLoginUtils.checkLogin(getApplication(), "视频详情") {} + return + } + if (topVideoDetail!!.me.isVoted) { + undoVoteVideo(topVideoDetail?.id ?: "") + } else { + if (topVideoDetail?.status == "pass") { + voteVideo(topVideoDetail?.id ?: "") + } else { + ToastUtils.toast("该作品正在审核中,暂时无法点赞哦") + } + } + } + + @SuppressLint("CheckResult") + private fun voteVideo(videoId: String) { + RetrofitManager.getInstance(getApplication()) + .api.voteVideo(videoId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + topVideoDetail?.let { + it.me.isVoted = true + it.count.vote = it.count.vote + 1 + handleSuccessLiveData.postValue(true) + ToastUtils.showToast("点赞爆棚,视频能让更多人看见!") + } + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + } + }) + } + + @SuppressLint("CheckResult") + private fun undoVoteVideo(videoId: String) { + RetrofitManager.getInstance(getApplication()) + .api.undoVoteVideo(videoId) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + topVideoDetail?.let { + it.me.isVoted = false + it.count.vote = it.count.vote - 1 + handleSuccessLiveData.postValue(true) + } + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + } + }) + } + + fun postFavorite() { + if (topVideoDetail == null) return + if (!UserManager.getInstance().isLoggedIn) { + CheckLoginUtils.checkLogin(getApplication(), "视频详情") {} + return + } + if (topVideoDetail!!.me.isVideoFavorite) { + undoFavoriteVideo(topVideoDetail?.id ?: "") + } else { + favoriteVideo(topVideoDetail?.id ?: "") + } + } + + @SuppressLint("CheckResult") + private fun favoriteVideo(videoId: String) { + RetrofitManager.getInstance(getApplication()) + .api.collectVideo(UserManager.getInstance().userId, videoId) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + topVideoDetail?.let { + it.me.isVideoFavorite = true + it.count.favorite++ + handleSuccessLiveData.postValue(true) + } + ToastUtils.toast("收藏成功") + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + Utils.toast(getApplication(), exception.localizedMessage) + } + }) + } + + @SuppressLint("CheckResult") + private fun undoFavoriteVideo(videoId: String) { + RetrofitManager.getInstance(getApplication()) + .api.undoCollectVideo(UserManager.getInstance().userId, videoId) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + topVideoDetail?.let { + it.me.isVideoFavorite = false + it.count.favorite-- + handleSuccessLiveData.postValue(true) + } + ToastUtils.toast("取消收藏") + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + Utils.toast(getApplication(), exception.localizedMessage) + } + }) + } + + fun followCommand() { + if (topVideoDetail == null) return + if (!UserManager.getInstance().isLoggedIn) { + CheckLoginUtils.checkLogin(getApplication(), "视频详情") {} + return + } + val observable = if (topVideoDetail?.me?.isFollower == false) { + RetrofitManager.getInstance(getApplication()).api.postFollowing(topVideoDetail?.user?.id) + } else { + RetrofitManager.getInstance(getApplication()).api.deleteFollowing(topVideoDetail?.user?.id) + } + observable + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + topVideoDetail?.let { + if (!it.me.isFollower) { + ToastUtils.showToast("关注成功") + } + it.me.isFollower = !it.me.isFollower + } + handleSuccessLiveData.postValue(true) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + ToastUtils.showToast("加载失败,请检查网络状态") + } + }) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoItemViewHolder.kt new file mode 100644 index 0000000000..961dffb697 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/desc/VideoItemViewHolder.kt @@ -0,0 +1,8 @@ +package com.gh.gamecenter.qa.video.detail.desc + +import com.gh.base.BaseRecyclerViewHolder +import com.gh.gamecenter.databinding.ItemForumVideoBinding +import com.gh.gamecenter.databinding.ItemVideoDescTopBinding +import com.gh.gamecenter.entity.ForumVideoEntity + +class VideoItemViewHolder(val binding: ItemForumVideoBinding) : BaseRecyclerViewHolder(binding.root) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishActivity.kt new file mode 100644 index 0000000000..e4220611aa --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishActivity.kt @@ -0,0 +1,55 @@ +package com.gh.gamecenter.qa.video.publish + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import com.gh.base.ToolBarActivity +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.entity.CommunityEntity +import com.gh.gamecenter.entity.ForumVideoEntity +import com.gh.gamecenter.entity.VideoDraftEntity +import com.gh.gamecenter.qa.BbsType + +class VideoPublishActivity : NormalActivity() { + override fun getLayoutId(): Int = R.layout.activity_video_publish + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mToolbar.navigationIcon = null + findViewById(R.id.backBtn).setOnClickListener { onBackPressed() } + } + + companion object { + @JvmStatic + fun getIntent(context: Context, communityEntity: CommunityEntity?, type: String, entrance: String, path: String): Intent { + val bundle = Bundle() + bundle.putParcelable(CommunityEntity::class.java.simpleName, communityEntity) + bundle.putString(BbsType::class.java.simpleName, type) + bundle.putString(EntranceUtils.KEY_ENTRANCE, ToolBarActivity.mergeEntranceAndPath(entrance, path)) + bundle.putString(EntranceUtils.KEY_PATH, path) + return getTargetIntent(context, VideoPublishActivity::class.java, VideoPublishFragment::class.java, bundle) + } + + @JvmStatic + fun getIntent(context: Context, videoEntity: ForumVideoEntity, entrance: String, path: String): Intent { + val bundle = Bundle() + bundle.putParcelable(ForumVideoEntity::class.java.simpleName, videoEntity) + bundle.putString(EntranceUtils.KEY_ENTRANCE, ToolBarActivity.mergeEntranceAndPath(entrance, path)) + bundle.putString(EntranceUtils.KEY_PATH, path) + return getTargetIntent(context, VideoPublishActivity::class.java, VideoPublishFragment::class.java, bundle) + } + + @JvmStatic + fun getDraftIntent(context: Context, draftEntity: VideoDraftEntity, entrance: String, path: String): Intent { + val bundle = Bundle() + bundle.putParcelable(VideoDraftEntity::class.java.simpleName, draftEntity) + bundle.putString(EntranceUtils.KEY_ENTRANCE, ToolBarActivity.mergeEntranceAndPath(entrance, path)) + bundle.putString(EntranceUtils.KEY_PATH, path) + return getTargetIntent(context, VideoPublishActivity::class.java, VideoPublishFragment::class.java, bundle) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishFragment.kt new file mode 100644 index 0000000000..2f077b35f6 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishFragment.kt @@ -0,0 +1,636 @@ +package com.gh.gamecenter.qa.video.publish + +import android.app.Activity +import android.content.Intent +import android.graphics.Bitmap +import android.media.MediaMetadataRetriever +import android.media.ThumbnailUtils +import android.net.Uri +import android.os.Bundle +import android.provider.MediaStore +import android.text.TextUtils +import android.view.Gravity +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.widget.doOnTextChanged +import com.gh.base.BaseRichEditorActivity +import com.gh.base.fragment.WaitingDialogFragment +import com.gh.common.AppExecutor +import com.gh.common.runOnUiThread +import com.gh.common.util.* +import com.gh.gamecenter.CropImageActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentVideoPublishBinding +import com.gh.gamecenter.entity.* +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.mvvm.Status +import com.gh.gamecenter.normal.NormalFragment +import com.gh.gamecenter.qa.BbsType +import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment +import com.gh.gamecenter.qa.dialog.ChooseForumActivity +import com.gh.gamecenter.qa.editor.GameActivity +import com.gh.gamecenter.qa.editor.LocalMediaActivity +import com.gh.gamecenter.video.poster.PosterEditActivity +import com.gh.gamecenter.video.upload.OnUploadListener +import com.gh.gamecenter.video.upload.UploadManager +import com.gh.gamecenter.video.upload.view.VideoFileEntity +import com.gh.gamecenter.video.videomanager.VideoDraftActivity +import com.lightgame.download.FileUtils +import java.io.File +import java.io.FileOutputStream + +class VideoPublishFragment : NormalFragment() { + private lateinit var mBinding: FragmentVideoPublishBinding + private lateinit var mViewModel: VideoPublishViewModel + private var mVideoFileEntity: VideoFileEntity? = null + private lateinit var mMenuDraft: MenuItem + private lateinit var mMenuPost: MenuItem + private var mUpdatedPosterPath = "" + private var mProcessingDialog: WaitingDialogFragment? = null + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout(): View { + mBinding = FragmentVideoPublishBinding.inflate(LayoutInflater.from(requireContext()), null, false) + return mBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setNavigationTitle("发视频") + initMenu(R.menu.menu_answer_post) + mMenuDraft = getItemMenu(R.id.menu_draft) + mMenuPost = getItemMenu(R.id.menu_answer_post) + mViewModel = viewModelProvider() + mViewModel.videoDraft = arguments?.getParcelable(VideoDraftEntity::class.java.simpleName) + mViewModel.videoPatch = arguments?.getParcelable(ForumVideoEntity::class.java.simpleName) + mViewModel.communityEntity = arguments?.getParcelable(CommunityEntity::class.java.simpleName) + mViewModel.type = arguments?.getString(BbsType::class.java.simpleName) ?: "" + initData() + observeData() + checkPostButtonEnable() + + mBinding.title.filters = arrayOf(TextHelper.getFilter(50, "标题最多50个字")) + mBinding.title.doOnTextChanged { text, start, _, _ -> + if (text?.contains("\n") == true) { + mBinding.title.setText(text.toString().replace("\n", "")) + mBinding.title.setSelection(start) + return@doOnTextChanged + } + if (PatternUtils.isHasSpace(text.toString())) { + mBinding.title.setText(PatternUtils.replaceSpace(text.toString())) + mBinding.title.setSelection(start) + return@doOnTextChanged + } + checkPostButtonEnable() + } + mBinding.videoDes.doOnTextChanged { _, _, _, _ -> + checkPostButtonEnable() + } + + mBinding.uploadButton.setOnClickListener { + PermissionHelper.checkStoragePermissionBeforeAction(requireActivity(), object : EmptyCallback { + override fun onCallback() { + startActivityForResult(LocalMediaActivity.getIntent(requireContext(), LocalMediaActivity.ChooseType.VIDEO, 1), BaseRichEditorActivity.INSERT_VIDEO_CODE) + } + }) + } + mBinding.videoPosterPatchHint.setOnClickListener { startMediaStore() } + mBinding.forumContainer.setOnClickListener { + if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + startActivityForResult(GameActivity.getIntent(requireContext(), GameActivity.SELECT_GAME_TITLE), REQUEST_GAME_CODE) + } else { + ChooseForumActivity.startChooseForumActivity(requireActivity()) + } + } + mBinding.chooseActivityContainer.setOnClickListener { + ChooseActivityDialogFragment.show(requireActivity() as AppCompatActivity, mViewModel.selectActivityLabelEntity?.id) { + mViewModel.selectActivityLabelEntity = it + mBinding.activityTitle.text = it.name + mBinding.activityTitle.setTextColor(R.color.text_FA8500.toColor()) + } + } + mBinding.deleteVideoIv.setOnClickListener { + DialogHelper.showDialog(requireContext(), "提示", "确定删除吗?", "确定", "取消", { + mVideoFileEntity = null + resetUI() + }) + } + } + + private fun initData() { + val videoPatch = mViewModel.videoPatch + val videoDraft = mViewModel.videoDraft + when { + videoPatch != null -> { + setNavigationTitle("编辑视频") + setVideoPatch(videoPatch) + } + videoDraft != null -> { + setNavigationTitle("发视频") + setVideoDraft() + } + else -> { + setNavigationTitle("发视频") + if (mViewModel.communityEntity != null) { + setForumName() + if (mViewModel.type == BbsType.GAME_BBS.value) { + mBinding.forumContainer.isEnabled = false + mBinding.chooseForumTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) + } + } + PermissionHelper.checkStoragePermissionBeforeAction(requireActivity(), object : EmptyCallback { + override fun onCallback() { + startActivityForResult(LocalMediaActivity.getIntent(requireContext(), LocalMediaActivity.ChooseType.VIDEO, 1), BaseRichEditorActivity.INSERT_VIDEO_CODE) + } + }) + } + } + } + + private fun observeData() { + mViewModel.processDialog.observeNonNull(this) { + if (it.isShow) { + if (mProcessingDialog?.dialog?.isShowing == true) { + mProcessingDialog?.uploadWaitingHint(it.msg) + } else { + mProcessingDialog = WaitingDialogFragment.newInstance(it.msg, false) + mProcessingDialog?.show(childFragmentManager, null) + } + } else { + mProcessingDialog?.dismiss() + } + } + mViewModel.postDraftLiveData.observeNonNull(this) { + if (it.status == Status.SUCCESS) { + toast("保存成功") + requireActivity().finish() + } else if (it.status == Status.ERROR) { + ErrorHelper.handleError(requireContext(), it.exception?.response()?.errorBody()?.string()) + } + } + mViewModel.postLiveData.observeNonNull(this) { + if (it.status == Status.SUCCESS) { + toast("提交成功,审核通过即可生效") + val data = it.data + val intent = Intent() + data?.title = mBinding.title.text.toString() + data?.des = mBinding.videoDes.text.toString() + intent.putExtra(ForumVideoEntity::class.java.simpleName, data) + requireActivity().setResult(Activity.RESULT_OK, intent) + requireActivity().finish() + AppExecutor.uiExecutor.executeWithDelay(Runnable { + NotificationHelper.showNotificationHintDialog(NotificationUgc.VIDEO) + }, 1000) + UploadManager.deleteUploadData(mVideoFileEntity?.path) + } else if (it.status == Status.ERROR) { + ErrorHelper.handleError(requireContext(), it.exception?.response()?.errorBody()?.string()) + } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (data == null || resultCode != Activity.RESULT_OK) return + if (requestCode == BaseRichEditorActivity.INSERT_VIDEO_CODE) { + val localVideoList = data.getParcelableArrayListExtra(LocalVideoEntity::class.java.name) + ?: arrayListOf() + if (localVideoList.isNotEmpty()) { + initUpload(localVideoList[0].filePath, localVideoList[0].poster) + } + } else if (requestCode == REQUEST_CODE_IMAGE_CROP) { + val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) + mBinding.videoPoster.setImageURI("file://$imagePath") + mUpdatedPosterPath = imagePath ?: "" + } else if (requestCode == ChooseForumActivity.CHOOSE_FORUM_REQUEST) { + val community = data.getParcelableExtra(EntranceUtils.KEY_COMMUNITY_DATA) + mViewModel.communityEntity = community + mViewModel.type = community?.type ?: "" + if (mViewModel.type == BbsType.GAME_BBS.value) { + mViewModel.gameEntity = null + } + setForumName() + } else if (requestCode == REQUEST_GAME_CODE) { + val game = data.getParcelableExtra(GameEntity::class.java.simpleName) + if (game != null) { + mViewModel.gameEntity = game + setForumName() + } + } else if (requestCode == REQUEST_CHOOSE_DRAFT) { + val draftEntity = data.getParcelableExtra(VideoDraftEntity::class.java.simpleName) + if (draftEntity != null) { + mViewModel.videoDraft = draftEntity + setVideoDraft() + } + } + } + + private fun setVideoPatch(videoPatch: ForumVideoEntity) { + mMenuDraft.isVisible = false + mViewModel.type = videoPatch.type + mViewModel.gameEntity = videoPatch.game + mViewModel.communityEntity = videoPatch.bbs + mViewModel.communityEntity?.icon = mViewModel.gameEntity?.icon + mViewModel.communityEntity?.iconSubscript = mViewModel.gameEntity?.iconSubscript + mBinding.forumContainer.isEnabled = false + mBinding.deleteVideoIv.visibility = View.GONE + mBinding.title.setText(videoPatch.title) + mBinding.videoDes.setText(videoPatch.des) + mBinding.activityTitle.text = videoPatch.tagActivityName + mBinding.chooseActivityContainer.isEnabled = false + ImageUtils.display(mBinding.videoPoster, videoPatch.poster) + if (mVideoFileEntity == null) { + mVideoFileEntity = VideoFileEntity("", videoPatch.url, videoPatch.poster, videoPatch.length, videoPatch.length, videoPatch.format) + } + handleUploadSuccess(videoPatch.url) + mBinding.deleteVideoIv.visibility = View.GONE + setForumName() + } + + private fun setVideoDraft() { + mViewModel.videoDraft?.let { + if (it.bbsId.isNotEmpty() && it.game != null) { + mViewModel.communityEntity = CommunityEntity(it.bbsId, it.game?.name + ?: "", icon = it.game?.icon, iconSubscript = it.game?.iconSubscript) + mViewModel.gameEntity = it.game + } + mViewModel.gameEntity = it.game + mViewModel.type = it.type + mBinding.title.setText(it.title) + mBinding.videoDes.setText(it.des) + if (it.tagActivityId.isNotEmpty() && it.tagActivityName.isNotEmpty()) { + mBinding.activityTitle.text = it.tagActivityName + mBinding.activityTitle.setTextColor(R.color.text_FA8500.toColor()) + mViewModel.selectActivityLabelEntity = ActivityLabelEntity(it.tagActivityId, it.tagActivityName) + } + if (it.url.isNotEmpty()) { + ImageUtils.display(mBinding.videoPoster, it.poster) + handleUploadSuccess(it.url) + } + initUpload(it.localPath, it.poster) + setForumName() + } + } + + private fun initUpload(videoPath: String?, poster: String) { + if (videoPath == null || !File(videoPath).exists()) { + handleFileNotFound() + return + } + if (poster.isNotEmpty()) { + ImageUtils.display(mBinding.videoPoster, poster) + } else { + val thumbnail = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Images.Thumbnails.MINI_KIND) + mBinding.videoPoster.setImageBitmap(thumbnail) + } + + mBinding.uploadButton.visibility = View.GONE + mBinding.videoPosterMask.visibility = View.GONE + mBinding.pauseButton.setOnClickListener { + when { + UploadManager.isUploading(videoPath) -> { + mBinding.uploadStatus.text = "上传已暂停" + mBinding.uploadInfoContainer.visibility = View.VISIBLE + mBinding.uploadSpeed.visibility = View.GONE + mBinding.pauseButton.setImageResource(R.drawable.upload_resume) + mBinding.pauseButton.visibility = View.VISIBLE + UploadManager.cancelTask(videoPath) + } + File(videoPath).exists() -> { + mBinding.uploadStatus.text = "视频上传中..." + mBinding.uploadInfoContainer.visibility = View.VISIBLE + mBinding.pauseButton.setImageResource(R.drawable.upload_pause) + mBinding.pauseButton.visibility = View.VISIBLE + createUploadTask(videoPath) + } + else -> { + handleFileNotFound() + toast("上传失败,视频文件不存在") + } + } + } + val videoFile = File(videoPath) + val retriever = MediaMetadataRetriever() + val fileUri = Uri.fromFile(videoFile) + retriever.setDataSource(requireContext(), fileUri) + val time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) ?: "0" + val timeInSecond = java.lang.Long.parseLong(time) / 1000 + retriever.release() + + var format = "" + tryWithDefaultCatch { + val mimeType = FileUtils.getFileMimeType(requireContext(), videoPath) + if (mimeType != null) { + val split = mimeType.split("/") + format = if (split.count() >= 2) { + split[1] + } else { + mimeType + } + } + } + + mVideoFileEntity = VideoFileEntity(videoPath, null, poster, timeInSecond, videoFile.length(), format) + createUploadTask(videoPath) + } + + private fun createUploadTask(videoPath: String) { + mBinding.uploadStatus.text = "视频上传中..." + UploadManager.createUploadTask(videoPath, object : OnUploadListener { + override fun onProgressChanged(uploadFilePath: String, currentSize: Long, totalSize: Long, speed: Long) { + runOnUiThread { + mBinding.uploadInfoContainer.visibility = View.VISIBLE + mBinding.uploadStatus.text = "视频上传中..." + mBinding.uploadSpeed.visibility = View.VISIBLE + mBinding.pauseButton.visibility = View.VISIBLE + mBinding.uploadSpeed.text = (SpeedUtils.getSpeed(speed) + "预计还需" + SpeedUtils.getRemainTime(totalSize, currentSize, speed)) + mBinding.uploadProgress.update(((360 * currentSize) / totalSize).toInt(), "") + } + } + + override fun onUploadSuccess(uploadFilePath: String, url: String) { + runOnUiThread { + handleUploadSuccess(url) + } + } + + override fun onUploadFailure(uploadFilePath: String, errorMsg: String) { + runOnUiThread { + if (!File(uploadFilePath).exists()) { + handleFileNotFound() + toast("上传失败,视频文件不存在") + } else { + mBinding.uploadStatus.text = "网络错误,中断上传" + mBinding.pauseButton.setImageResource(R.drawable.upload_resume) + mBinding.pauseButton.visibility = View.VISIBLE + toast("网络错误,请检查网络正常后再重试") + } + mBinding.uploadSpeed.visibility = View.GONE + } + } + }) + } + + private fun handleFileNotFound() { + mBinding.uploadButton.visibility = View.VISIBLE + mBinding.uploadInfoContainer.visibility = View.GONE + mBinding.videoPosterMask.visibility = View.VISIBLE + } + + private fun handleUploadSuccess(url: String) { + mBinding.uploadButton.visibility = View.GONE + mBinding.uploadInfoContainer.visibility = View.GONE + mBinding.videoPosterMask.visibility = View.GONE + mBinding.videoPosterPatchHint.visibility = View.VISIBLE + mBinding.deleteVideoIv.visibility = View.VISIBLE + mBinding.uploadProgress.update(360, "") + mVideoFileEntity?.url = url + checkPostButtonEnable() + } + + private fun resetUI() { + mBinding.deleteVideoIv.visibility = View.GONE + mBinding.uploadButton.visibility = View.VISIBLE + mBinding.videoPosterMask.visibility = View.VISIBLE + mBinding.uploadInfoContainer.visibility = View.GONE + mBinding.videoPosterPatchHint.visibility = View.GONE + checkPostButtonEnable() + } + + private fun setForumName() { + if (mViewModel.communityEntity != null) { + if (mViewModel.type == BbsType.GAME_BBS.value) { + mBinding.chooseForumTv.text = mViewModel.communityEntity?.name + mBinding.forumIconView.displayGameIcon(mViewModel.communityEntity?.icon, mViewModel.communityEntity?.iconSubscript) + setForumUI() + } else if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + if (mViewModel.gameEntity == null) { + mBinding.chooseForumTv.text = "选择游戏" + } else { + mBinding.chooseForumTv.text = mViewModel.gameEntity?.name + mBinding.forumIconView.displayGameIcon(mViewModel.gameEntity?.icon, mViewModel.gameEntity?.iconSubscript) + setForumUI() + } + } + } else { + if (mViewModel.type == BbsType.GAME_BBS.value) { + mBinding.chooseForumTv.text = "选择论坛" + } else if (mViewModel.type == BbsType.OFFICIAL_BBS.value) { + mBinding.chooseForumTv.text = "选择游戏" + } + } + checkPostButtonEnable() + } + + private fun setForumUI() { + mBinding.forumIconView.visibility = View.VISIBLE + mBinding.forumContainer.background = ContextCompat.getDrawable(requireContext(), R.drawable.bg_shape_f8_radius_4) + mBinding.chooseForumTv.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(requireContext(), R.drawable.ic_article_edit_choose_forum_arrow_gray), null) + mBinding.chooseForumTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_333333)) + } + + private fun checkPostButtonEnable() { + mMenuPost.actionView.postDelayed({ + val isEnabled = checkData() + mMenuPost.actionView.alpha = if (isEnabled) 1f else 0.6f + }, 100) + } + + private fun checkData(): Boolean { + val title = mBinding.title.text.toString() + val des = mBinding.videoDes.text.toString() + if (mVideoFileEntity?.url.isNullOrEmpty()) return false + if (mViewModel.communityEntity == null) return false + if (mViewModel.type == BbsType.OFFICIAL_BBS.value && mViewModel.gameEntity == null) return false + if (title.isEmpty()) return false + if (des.isEmpty()) return false + if (mViewModel.videoPatch != null && mViewModel.videoPatch?.title == title && mViewModel.videoPatch?.des == des) return false + return true + } + + override fun onMenuItemClick(menuItem: MenuItem?) { + super.onMenuItemClick(menuItem) + when (menuItem?.itemId) { + R.id.menu_answer_post -> { + if (checkData()){ + verifyData(false) + } + } + R.id.menu_draft -> { + startActivityForResult(VideoDraftActivity.getIntent(requireContext()), REQUEST_CHOOSE_DRAFT) + } + } + } + + private fun startMediaStore() { + try { + PermissionHelper.checkStoragePermissionBeforeAction(requireContext(), object : EmptyCallback { + override fun onCallback() { + var intent: Intent? = null + when { + mVideoFileEntity?.url?.isNotEmpty() == true -> { + val videoEntity = VideoEntity(length = mVideoFileEntity?.length + ?: 0, url = mVideoFileEntity?.url ?: "") + intent = PosterEditActivity.getIntentByVideo(requireContext(), videoEntity) + } + mVideoFileEntity?.path?.isNotEmpty() == true -> { + intent = PosterEditActivity.getIntentByPath(requireContext(), mVideoFileEntity?.url + ?: "") + } + else -> { + throwExceptionInDebug("video not found") + } + } + startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP) + } + }) + } catch (e: Exception) { + toast(R.string.media_image_hint) + e.printStackTrace() + } + } + + override fun onBackPressed(): Boolean { + if (TextUtils.isEmpty(UserManager.getInstance().token)) return false + + if (mViewModel.videoPatch == null && mViewModel.videoDraft == null) { + if (mBinding.title.text.isNotEmpty() || mBinding.videoDes.text.isNotEmpty()) { + DialogUtils.showNewAlertDialog(requireContext(), "提示", "是否保存内容再退出?", "不保存", "保存并退出", Gravity.CENTER, true, { + requireActivity().finish() + }, { + verifyData(true) + }) + return true + } + } + + if (mViewModel.videoPatch != null && mViewModel.videoDraft == null) { + return if (mViewModel.videoPatch?.title != mBinding.title.text.toString() || + mViewModel.videoPatch?.des != mBinding.videoDes.text.toString()) { + showBackDialog() + true + } else false + + } + + return checkDraft() + } + + private fun checkDraft(): Boolean { + if (mViewModel.videoDraft != null && (mViewModel.videoDraft?.title != mBinding.title.text.toString() + || mViewModel.videoDraft?.des != mBinding.videoDes.text.toString())) { + showBackDialog() + return true + } + return false + } + + private fun showBackDialog() { + DialogUtils.showNewAlertDialog(requireContext(), "提示", "是否保存修改内容用于下次编辑?", "不保存", "保存并退出", Gravity.CENTER, true, { + requireActivity().finish() + }, { + verifyData(true) + }) + } + + private fun verifyData(isDraft: Boolean) { + if (ClickUtils.isFastDoubleClick()) return + if (mViewModel.videoPatch == null) { + if (!isDraft && mVideoFileEntity != null && + !File(mVideoFileEntity?.path ?: "").exists()) { + toast("提交失败,视频文件不存在") + handleFileNotFound() + return + } + + if (!isDraft && mVideoFileEntity?.url == null) { + toast("视频尚未上传完成") + return + } + + if (!isDraft && (mViewModel.communityEntity == null || mViewModel.communityEntity?.id?.isEmpty() == true)) { + toast("请选择论坛") + return + } + if (!isDraft && mViewModel.type == BbsType.OFFICIAL_BBS.value && mViewModel.gameEntity == null) { + toast("请选择游戏") + return + } + } + val title = mBinding.title.text.trim() + if (!isDraft && title.isEmpty()) { + toast("请填写标题") + return + } + val des = mBinding.videoDes.text.trim() + + val videoData = if (mViewModel.videoPatch != null) { + mViewModel.videoPatch!!.title = title.toString() + mViewModel.videoPatch!!.des = des.toString() + mViewModel.videoPatch!! + } else { + ForumVideoEntity( + id = mViewModel.videoDraft?.id ?: "", + title = title.toString(), + des = des.toString(), + url = mVideoFileEntity?.url ?: "", + size = mVideoFileEntity?.size ?: 0, + format = mVideoFileEntity?.format ?: "", + length = mVideoFileEntity?.length ?: 0, + poster = mViewModel.videoDraft?.poster ?: "", + bbsId = mViewModel.communityEntity?.id ?: "", + type = mViewModel.type, + gameId = mViewModel.gameEntity?.id ?: mViewModel.communityEntity?.game?.id + ?: "", + tagActivityId = mViewModel.selectActivityLabelEntity?.id ?: "", + tagActivityName = mViewModel.selectActivityLabelEntity?.name ?: "" + ) + } + if (mUpdatedPosterPath.isEmpty() && videoData.poster.isNotEmpty()) { + mViewModel.postContent(isDraft, videoData, mVideoFileEntity?.path) + } else { + //未选择视频直接保存草稿 + if (isDraft && mVideoFileEntity == null) { + mViewModel.postContent(isDraft, videoData, mVideoFileEntity?.path) + return + } + val posterPath = if (mUpdatedPosterPath.isNotEmpty()) { + mUpdatedPosterPath + } else { + val localVideoPoster = requireActivity().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg" + try { + val bmp = ThumbnailUtils.createVideoThumbnail(mVideoFileEntity?.path + ?: "", MediaStore.Images.Thumbnails.MINI_KIND) + // bmp 可能为空 + FileOutputStream(localVideoPoster).use { out -> + bmp?.compress(Bitmap.CompressFormat.PNG, 100, out) + } + } catch (e: java.lang.Exception) { + e.printStackTrace() + toast("视频封面操作失败") + return + } + localVideoPoster + } + mViewModel.postVideoPosterAndContent(isDraft, videoData, posterPath, mVideoFileEntity?.path) + } + } + + override fun onDestroy() { + super.onDestroy() + val path = mVideoFileEntity?.path + if (path != null && UploadManager.isUploading(path)) { + UploadManager.cancelTask(path) + } + } + + companion object { + const val REQUEST_CODE_IMAGE_CROP = 101 + const val REQUEST_GAME_CODE = 102 + const val REQUEST_CHOOSE_DRAFT = 103 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishViewModel.kt new file mode 100644 index 0000000000..cd49b7a1d5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishViewModel.kt @@ -0,0 +1,174 @@ +package com.gh.gamecenter.qa.video.publish + +import android.annotation.SuppressLint +import android.app.Application +import android.media.MediaMetadataRetriever +import android.net.Uri +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MediatorLiveData +import com.gh.base.fragment.WaitingDialogFragment +import com.gh.common.util.* +import com.gh.gamecenter.entity.* +import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.mvvm.Resource +import com.gh.gamecenter.retrofit.BiResponse +import com.gh.gamecenter.retrofit.Response +import com.gh.gamecenter.retrofit.RetrofitManager +import com.google.gson.JsonObject +import com.lightgame.utils.Utils +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.MediaType +import okhttp3.RequestBody +import okhttp3.ResponseBody +import org.json.JSONObject +import retrofit2.HttpException +import java.io.File + +class VideoPublishViewModel(application: Application) : AndroidViewModel(application) { + private val mApi = RetrofitManager.getInstance(getApplication()).api + var type: String = "" + var communityEntity: CommunityEntity? = null + var selectActivityLabelEntity: ActivityLabelEntity? = null + var videoPatch: ForumVideoEntity? = null + var videoDraft: VideoDraftEntity? = null + var gameEntity: GameEntity? = null + val postDraftLiveData = MediatorLiveData>() + val processDialog = MediatorLiveData() + val postLiveData = MediatorLiveData>() + + fun postVideoPosterAndContent(isDraft: Boolean, videoEntity: ForumVideoEntity, videoPoster: String, videoPath: String?) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传视频封面中...", true)) + val application: Application = getApplication() + UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster, videoPoster, true, + object : UploadImageUtils.OnUploadImageListener { + override fun onSuccess(imageUrl: String) { + videoEntity.poster = imageUrl + postContent(isDraft, videoEntity, videoPath) + } + + override fun onError(e: Throwable?) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + if (e != null && e is HttpException && e.code() == 403) { + Utils.toast(getApplication(), "图片违规,请重新编辑") + } else { + Utils.toast(application, "保存失败,请检查网络正常后再重试") + } + } + + override fun onProgress(total: Long, progress: Long) { + } + }) + } + + fun postContent(isDraft: Boolean, videoEntity: ForumVideoEntity, videoPath: String?) { + when { + videoPatch != null -> { + patchVideo(JSONObject(GsonUtils.toJson(videoEntity)), videoEntity.id) + } + isDraft -> { + val draftData = JSONObject(videoEntity.toJson()).put("local_path", videoPath).toString() + val body = RequestBody.create(MediaType.parse("application/json"), draftData) + postDraft(body) + } + else -> { + val requestJson = JSONObject(GsonUtils.toJson(videoEntity)) + var videoHeight = 0 + var videoWidth = 0 + videoPath?.let { + tryCatchInRelease { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(getApplication(), Uri.fromFile(File(it))) + videoWidth = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH) + ?: "") + videoHeight = Integer.valueOf(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT) + ?: "") + retriever.release() + } + } + val videoInfo = JSONObject() + videoInfo.put("height", videoHeight) + videoInfo.put("width", videoWidth) + requestJson.put("video_info", videoInfo) + + postVideo(requestJson) + } + } + } + + private fun patchVideo(jsonObject: JSONObject, videoId: String) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) + val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString()) + mApi.patchBbsVideo(videoId, body) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + val poster = jsonObject.getString("poster") + val url = jsonObject.getString("url") + val length = jsonObject.getLong("length") + postLiveData.postValue(Resource.success(ForumVideoEntity(id = videoId, poster = poster, url = url, length = length, status = "pending"))) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + postLiveData.postValue(Resource.error(e)) + } + }) + } + + @SuppressLint("CheckResult") + private fun postDraft(body: RequestBody) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) + val postVideoDraft = if (videoDraft != null) { + mApi.patchVideoDraft(UserManager.getInstance().userId, body, videoDraft?.id) + } else { + mApi.postVideoDraft(UserManager.getInstance().userId, body) + } + postVideoDraft + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + postDraftLiveData.postValue(Resource.success("")) + } + + override fun onFailure(exception: Exception) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + postDraftLiveData.postValue(Resource.error(null)) + } + + }) + } + + private fun postVideo(jsonObject: JSONObject) { + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) + val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString()) + mApi.postBbsVideo(body) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: JsonObject?) { + super.onResponse(response) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + tryWithDefaultCatch { + response?.let { + val videoId = it.get("_id").asString + val poster = it.get("poster").asString + val url = it.get("url").asString + val length = it.get("length").asLong + postLiveData.postValue(Resource.success(ForumVideoEntity(id = videoId, poster = poster, url = url, length = length, status = "pending"))) + } + } + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false)) + postLiveData.postValue(Resource.error(e)) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 4d7b9b2510..761c82bb40 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -21,9 +21,13 @@ import com.gh.gamecenter.entity.ConcernEntity; import com.gh.gamecenter.entity.DeviceDialogEntity; import com.gh.gamecenter.entity.EnergyTaskCompleteEntity; import com.gh.gamecenter.entity.FollowersOrFansEntity; +import com.gh.gamecenter.entity.ForumActivityCategoryEntity; +import com.gh.gamecenter.entity.ForumActivityEntity; import com.gh.gamecenter.entity.ForumCategoryEntity; import com.gh.gamecenter.entity.ForumDetailEntity; import com.gh.gamecenter.entity.ForumEntity; +import com.gh.gamecenter.entity.ForumUnreadEntity; +import com.gh.gamecenter.entity.ForumVideoEntity; import com.gh.gamecenter.entity.FunctionalGroupEntity; import com.gh.gamecenter.entity.GameColumnCollection; import com.gh.gamecenter.entity.GameDetailEntity; @@ -116,7 +120,6 @@ import com.gh.gamecenter.qa.entity.QuestionsIndexEntity; import com.gh.gamecenter.qa.entity.SearchHottestEntity; import com.gh.gamecenter.qa.entity.SearchNewestEntity; import com.gh.gamecenter.qa.entity.SuggestedFollowEntity; -import com.gh.gamecenter.retrofit.Response; import com.google.gson.JsonObject; import java.util.ArrayList; @@ -760,8 +763,8 @@ public interface ApiService { /** * 社区问题详情 */ - @GET("questions/{questions_id}?view=detail") - Observable getQuestionsById(@Path("questions_id") String questionsId); + @GET("bbses/questions/{question_id}?view=detail") + Observable getQuestionsById(@Path("question_id") String questionsId); /** * 社区问题的回答列表 @@ -877,13 +880,13 @@ public interface ApiService { /** * 添加社区问题 */ - @POST("communities/{community_id}/questions") - Observable postQuestions(@Body RequestBody body, @Path("community_id") String communityId); + @POST("bbses/questions") + Observable postQuestions(@Body RequestBody body); /** - * 添加社区问题 + * 编辑社区问题 */ - @POST("questions/{question_id}") + @PUT("bbses/questions/{question_id}") Observable patchQuestions(@Body RequestBody body, @Path("question_id") String questionId); /** @@ -1448,6 +1451,12 @@ public interface ApiService { @GET("users/{user_id}/communities/articles?view=home_page") Observable> getMyArticle(@Path("user_id") String userId, @Query("page") int page); + /** + * 获取社区文章问题列表 + */ + @GET("users/{user_id}/communities/articles?view=home_page&type=article-question") + Observable> getMyArticleAndQuestion(@Path("user_id") String userId, @Query("page") int page); + /** * 获取用户的文章草稿列表 */ @@ -1508,6 +1517,12 @@ public interface ApiService { @GET("users/{user_id}/favorites/communities/articles") Observable> getCollectionCommunityArticle(@Path("user_id") String userId, @Query("page") int page); + /** + * 获取收藏的社区文章/问题列表 + */ + @GET("users/{user_id}/favorites/communities/articles?type=article-question") + Observable> getCollectionArticleAndQuestion(@Path("user_id") String userId, @Query("page") int page); + /** * 收藏社区文章 */ @@ -2096,6 +2111,12 @@ public interface ApiService { @POST("shares") Single postShareResult(@Body RequestBody body); + /** + * 社区内容浏览记录 + */ + @POST("browses") + Single postBrowses(@Body RequestBody body); + /** * 给一个视频新增评论 */ @@ -2124,7 +2145,7 @@ public interface ApiService { * 获取评论列表.可以分页 */ @GET("videos/{video_id}/comments") - Observable> getVideoCommentList(@Path("video_id") String videoId, @Query("page") int page); + Observable> getVideoCommentList(@Path("video_id") String videoId, @Query("page") int page, @QueryMap Map params); /** * 获取评论的对话列表. @@ -2626,6 +2647,30 @@ public interface ApiService { @GET("users/{user_id}/follows/bbses") Observable> getFollowsForum(@Path("user_id") String userId); + /** + * 热门论坛列表 + */ + @GET("./bbses:hot") + Observable> getHotForum(); + + /** + * 搜索论坛 + */ + @GET("./bbses:search_name") + Observable> searchBbs(@Query("keyword") String keyword, @Query("page") int page); + + /** + * 热门论坛列表 + */ + @GET("./bbses:hot") + Observable> getHotForumWithPage(@Query("page") int page); + + /** + * 官方论坛列表 + */ + @GET("./bbses:official") + Observable> getOfficialForum(@Query("page") int page); + /** * 获取论坛分类 */ @@ -2659,8 +2704,8 @@ public interface ApiService { /** * 获取论坛推荐Tab内容 */ - @GET("bbses/recommends/contents") - Observable> getForumRecommendsArticle(@Query("sort") String sort, @Query("page") int page); + @GET("bbses/recommends") + Observable> getForumRecommends(@Query("sort") String sort, @Query("page") int page); /** * 获取论坛详情 @@ -2686,6 +2731,12 @@ public interface ApiService { @GET("bbses/{bbs_id}/questions?view=digest") Observable> getAskForumList(@Path("bbs_id") String bbsId, @Query("page") int page); + /** + * 获取论坛问答Tab内容(视频) + */ + @GET("bbses/{bbs_id}/videos") + Observable> getVideoForumList(@Path("bbs_id") String bbsId, @Query("sort") String sort, @Query("page") int page); + /** * 获取论坛版主 */ @@ -2786,13 +2837,13 @@ public interface ApiService { * 置顶评论 */ @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:set-top") - Observable commentTop(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commentId, @QueryMap Map params); + Observable postArticleCommentTop(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commentId, @QueryMap Map params); /** * 取消置顶评论 */ @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:unset-top") - Observable commentUnTop(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commentId); + Observable postArticleCommentUnTop(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commentId); /** * 获取推荐的论坛 @@ -2893,31 +2944,31 @@ public interface ApiService { /** * 新增一个问题草稿 */ - @POST("users/{user_id}/question_drafts") + @POST("users/{user_id}/bbses/question_drafts") Single addQuestionDraft(@Path("user_id") String userId, @Body RequestBody body); /** * 获取单个问题草稿 */ - @GET("users/{user_id}/question_drafts/{draft_id}") + @GET("users/{user_id}/bbses/question_drafts/{draft_id}") Single getQuestionDraft(@Path("user_id") String userId, @Path("draft_id") String draftId); /** * 修改一个问题草稿 */ - @POST("users/{user_id}/question_drafts/{draft_id}") + @PUT("users/{user_id}/bbses/question_drafts/{draft_id}") Single updateQuestionDraft(@Path("user_id") String userId, @Path("draft_id") String draftId, @Body RequestBody body); /** * 获取问题草稿列表 */ - @GET("users/{user_id}/question_drafts") + @GET("users/{user_id}/bbses/question_drafts") Observable> getQuestionDrafts(@Path("user_id") String userId); /** * 删除单个问题草稿 */ - @DELETE("users/{user_id}/question_drafts/{draft_id}") + @DELETE("users/{user_id}/bbses/question_drafts/{draft_id}") Observable deleteQuestionDraft(@Path("user_id") String userId, @Path("draft_id") String draftId); /** @@ -2988,4 +3039,144 @@ public interface ApiService { */ @GET("games/recommend_popup") Single> getRecommendPopup(@Query("gameId") String gameId); + + + /** + * 论坛视频投稿 + */ + @POST("bbses/videos") + Observable postBbsVideo(@Body RequestBody body); + + /** + * 修改论坛视频 + */ + @POST("bbses/videos/{video_id}") + Observable patchBbsVideo(@Path("video_id") String videoId, @Body RequestBody body); + + /** + * 获取论坛视频详情 + */ + @GET("bbses/videos/{video_id}?view=detail") + Observable getBbsVideoDetail(@Path("video_id") String videoId); + + /** + * 提交社区已读 + */ + @POST("bbses/{bbs_id}/enter_logs") + Single postForumRead(@Path("bbs_id") String forumId); + + /** + * 获取社区的未读状态 + */ + @POST("bbses/unread") + Single> getForumUnreadStatus(@Body RequestBody body); + + /** + * 获取视频详情推荐视频 + */ + @GET("bbses/videos/{video_id}/recommends") + Observable> getRecommendVideo(@Path("video_id") String videoId, @Query("page") int page); + + /** + * 添加问题收藏 + */ + @POST("users/{user_id}/favorites/bbses/questions/{question_id}") + Single favoriteQuestion(@Path("user_id") String userId, @Path("question_id") String questionId); + + /** + * 取消问题收藏 + */ + @DELETE("users/{user_id}/favorites/bbses/questions/{question_id}") + Single deleteFavoriteQuestion(@Path("user_id") String userId, @Path("question_id") String questionId); + + /** + * 问题评论(回答)列表 + */ + @GET("bbses/questions/{question_id}/comments") + Observable> getQuestionComment(@Path("question_id") String questionId, @Query("sort") String type, @Query("page") int page, @QueryMap Map params); + + /** + * 对社区问题发布评论(回答) + */ + @POST("bbses/questions/{question_id}/comments") + Observable postQuestionComment(@Path("question_id") String questionsId, @Body RequestBody body); + + + /** + * 回复社区问题评论(回答) + */ + @POST("bbses/questions/{question_id}/comments/{comment_id}:reply") + Observable postReplyToQuestionComment(@Path("question_id") String questionsId, @Path("comment_id") String commentId, @Body RequestBody body); + + /** + * 获取问题评论(回答) + */ + @GET("bbses/questions/{question_id}/comments/{comment_id}") + Single getCommunityQuestionComment(@Path("question_id") String questionId, @Path("comment_id") String commentId); + + /** + * 获取问题评论(回答)的回复(评论)列表 + */ + @GET("bbses/questions/{question_id}/comments/{comment_id}/replies") + Single> getQuestionCommentReply(@Path("question_id") String questionId, @Path("comment_id") String commentId, @Query("sort") String sort, @Query("page") int page); + + /** + * 点赞问题评论(回答) + */ + @POST("bbses/questions/{question_id}/comments/{comment_id}:vote") + Observable postVoteQuestionComment(@Path("question_id") String questioIid, @Path("comment_id") String commentId); + + /** + * 投诉问题的评论 + */ + @POST("bbses/questions/{question_id}/comments/{comment_id}:report") + Observable postQuestionCommentReport(@Path("question_id") String questionId, @Path("comment_id") String commentId, @Body RequestBody reportData); + + /** + * 删除问题评论 + */ + @POST("bbses/questions/{question_id}/comments/{comment_id}:hide") + Single deleteQuestionComment(@Path("question_id") String questionId, @Path("comment_id") String commentId); + + /** + * 置顶问题评论 + */ + @POST("bbses/questions/{question_id}/comments/{comment_id}:set-top") + Observable postQuestionCommentTop(@Path("question_id") String questionId, @Path("comment_id") String commentId, @QueryMap Map params); + + /** + * 取消问题置顶评论 + */ + @POST("bbses/questions/{question_id}/comments/{comment_id}:unset-top") + Observable postQuestionCommentUnTop(@Path("question_id") String questionId, @Path("comment_id") String commentId); + + /** + * 获取社区活动分类列表 + */ + @GET("videos/activities/category") + Single> getForumActivityCategories(); + + /** + * 获取社区活动列表 + */ + @GET("videos/activities") + Single> getForumActivities(@Query("filter") String filter, @Query("page") int page); + + /** + * 获取单条视频评论 + */ + @GET("videos/{video_id}/comments/{comment_id}") + Single getCommunityVideoComment(@Path("video_id") String videoId, @Path("comment_id") String commentId); + + /** + * 获取视频评论回复 + */ + @GET("videos/{video_id}/comments/{comment_id}/replies") + Single> getVideoCommentReply(@Path("video_id") String videoId, @Path("comment_id") String commentId, @Query("sort") String sort, @Query("page") int page); + + /** + * 加精视频贴 + */ + @POST("bbses/{bbs_id}/videos/{video_id}:choiceness") + Observable highlightCommunityVideo(@Path("bbs_id") String bbsId, @Path("video_id") String videoId); } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java index 12b9b51c84..fe59c96bc7 100644 --- a/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java +++ b/app/src/main/java/com/gh/gamecenter/room/AppDatabase.java @@ -13,16 +13,20 @@ import androidx.sqlite.db.SupportSQLiteDatabase; import com.gh.common.videolog.VideoRecordDao; import com.gh.common.videolog.VideoRecordEntity; import com.gh.gamecenter.entity.CommentDraft; +import com.gh.gamecenter.entity.ForumEntity; import com.gh.gamecenter.entity.HomePluggableFilterEntity; import com.gh.gamecenter.entity.SignEntity; import com.gh.gamecenter.entity.SimulatorGameRecordEntity; import com.gh.gamecenter.qa.entity.AnswerEntity; import com.gh.gamecenter.room.converter.ApkArrayListConverter; +import com.gh.gamecenter.room.converter.SimpleGameConverter; import com.gh.gamecenter.room.converter.SimulatorConverter; import com.gh.gamecenter.room.converter.StringArrayListConverter; import com.gh.gamecenter.room.converter.TagStyleListConverter; +import com.gh.gamecenter.room.converter.VideoInfoConverter; import com.gh.gamecenter.room.dao.AnswerDao; import com.gh.gamecenter.room.dao.CommentDraftDao; +import com.gh.gamecenter.room.dao.ForumDao; import com.gh.gamecenter.room.dao.HomePluggableFilterDao; import com.gh.gamecenter.room.dao.SignDao; import com.gh.gamecenter.room.dao.SimulatorGameDao; @@ -39,12 +43,15 @@ import com.gh.gamecenter.video.upload.UploadEntity; CommentDraft.class, HomePluggableFilterEntity.class, VideoRecordEntity.class, - SimulatorGameRecordEntity.class}, version = 18, exportSchema = false) + SimulatorGameRecordEntity.class, + ForumEntity.class}, version = 19, exportSchema = false) @TypeConverters({ StringArrayListConverter.class, TagStyleListConverter.class, SimulatorConverter.class, - ApkArrayListConverter.class + ApkArrayListConverter.class, + SimpleGameConverter.class, + VideoInfoConverter.class }) public abstract class AppDatabase extends RoomDatabase { @@ -62,6 +69,8 @@ public abstract class AppDatabase extends RoomDatabase { public abstract SimulatorGameDao simulatorGameDao(); + public abstract ForumDao forumDao(); + private static AppDatabase sInstance; private static final String DATABASE_NAME = "gh-db"; @@ -187,6 +196,18 @@ public abstract class AppDatabase extends RoomDatabase { } }; + static final Migration MIGRATION_18_19 = new Migration(18, 19) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE ForumEntity (id TEXT NOT NULL PRIMARY KEY, game TEXT NOT NULL, name TEXT NOT NULL, icon TEXT NOT NULL, isFollow INTEGER NOT NULL, isRecommend INTEGER NOT NULL, orderTag INTEGER NOT NULL, unread INTEGER NOT NULL, type TEXT NOT NULL)"); + database.execSQL("Alter TABLE AnswerEntity add des TEXT NOT NULL DEFAULT ''"); + database.execSQL("Alter TABLE AnswerEntity add url TEXT NOT NULL DEFAULT ''"); + database.execSQL("Alter TABLE AnswerEntity add videoInfo TEXT NOT NULL DEFAULT ''"); + database.execSQL("Alter TABLE AnswerEntity add poster TEXT NOT NULL DEFAULT ''"); + database.execSQL("Alter TABLE AnswerEntity add length INTEGER NOT NULL DEFAULT 0"); + } + }; + private static AppDatabase buildDatabase(Context context) { return Room.databaseBuilder(context, AppDatabase.class, DATABASE_NAME) .addMigrations( @@ -204,7 +225,8 @@ public abstract class AppDatabase extends RoomDatabase { MIGRATION_14_15, MIGRATION_15_16, MIGRATION_16_17, - MIGRATION_17_18 + MIGRATION_17_18, + MIGRATION_18_19 ) // 不允许主线程查询 .allowMainThreadQueries() diff --git a/app/src/main/java/com/gh/gamecenter/room/converter/SimpleGameConverter.kt b/app/src/main/java/com/gh/gamecenter/room/converter/SimpleGameConverter.kt new file mode 100644 index 0000000000..2ae56bf945 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/room/converter/SimpleGameConverter.kt @@ -0,0 +1,18 @@ +package com.gh.gamecenter.room.converter + +import androidx.room.TypeConverter +import com.gh.common.util.toJson +import com.gh.common.util.toObject +import com.gh.gamecenter.entity.SimpleGame + +class SimpleGameConverter { + @TypeConverter + fun toSimpleGameString(data: SimpleGame?): String { + return data?.toJson() ?: "" + } + + @TypeConverter + fun toSimpleGameEntity(token: String?): SimpleGame { + return token?.toObject() ?: SimpleGame() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/room/converter/VideoInfoConverter.java b/app/src/main/java/com/gh/gamecenter/room/converter/VideoInfoConverter.java new file mode 100644 index 0000000000..a938a6dbe8 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/room/converter/VideoInfoConverter.java @@ -0,0 +1,23 @@ +package com.gh.gamecenter.room.converter; + +import androidx.room.TypeConverter; + +import com.gh.gamecenter.entity.UserEntity; +import com.gh.gamecenter.entity.VideoInfo; +import com.google.gson.Gson; + +/** + * Created by khy on 18/04/18. + */ + +public class VideoInfoConverter { + @TypeConverter + public static String toVideoInfoString(VideoInfo data) { + return new Gson().toJson(data); + } + + @TypeConverter + public static VideoInfo toVideoInfo(String token) { + return new Gson().fromJson(token, VideoInfo.class); + } +} diff --git a/app/src/main/java/com/gh/gamecenter/room/dao/ForumDao.kt b/app/src/main/java/com/gh/gamecenter/room/dao/ForumDao.kt new file mode 100644 index 0000000000..92be2d4d8a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/room/dao/ForumDao.kt @@ -0,0 +1,19 @@ +package com.gh.gamecenter.room.dao + +import androidx.room.* +import com.gh.gamecenter.entity.ForumEntity +import io.reactivex.Single + +@Dao +interface ForumDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addForum(forum: ForumEntity) + + @Query("select * from ForumEntity order by orderTag desc") + fun getForum(): Single> + + @Delete + fun deleteForum(answer: ForumEntity) + +} diff --git a/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingActivity.kt b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingActivity.kt new file mode 100644 index 0000000000..9cb51ddd3b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingActivity.kt @@ -0,0 +1,26 @@ +package com.gh.gamecenter.setting +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity +import com.gh.gamecenter.R + +class GameDownloadSettingActivity : NormalActivity() { + + companion object { + + @JvmStatic + fun getIntent(context: Context, entrance: String): Intent { + val bundle = Bundle() + bundle.putString(EntranceUtils.KEY_ENTRANCE, entrance) + return getTargetIntent(context, GameDownloadSettingActivity::class.java, GameDownloadSettingFragment::class.java, bundle) + } + + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setNavigationTitle(R.string.setting_game_download) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt new file mode 100644 index 0000000000..318163fbde --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt @@ -0,0 +1,113 @@ +package com.gh.gamecenter.setting + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.text.TextUtils +import android.util.Log +import android.view.View +import butterknife.OnClick +import com.gh.common.util.MtaHelper.onEvent +import com.gh.common.util.PackageUtils +import com.gh.common.util.SPUtils +import com.gh.gamecenter.CleanApkActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentGameDownloadSettingBinding +import com.gh.gamecenter.normal.NormalFragment +import java.io.File + +class GameDownloadSettingFragment: NormalFragment() { + + private var mBinding: FragmentGameDownloadSettingBinding? = null + + override fun getLayoutId() = 0 + + override fun getInflatedLayout() = FragmentGameDownloadSettingBinding.inflate(layoutInflater).apply { mBinding = this }.root + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initStatus() + } + + private fun initStatus() { + mBinding?.run { + autoInstallSwitch.isChecked = SPUtils.getBoolean(AUTO_INSTALL_SP_KEY) + concernGameSwitch.isChecked = SPUtils.getBoolean(CONCERN_GAME_SP_KEY) + trafficSwitch.isChecked = SPUtils.getBoolean(getTrafficDownloadHintKey()) + } + } + + @OnClick(R.id.autoInstallContainer, R.id.concernGameContainer, R.id.trafficContainer, + R.id.downloadPathContainer, R.id.picPathContainer, R.id.cleanPackageContainer) + override fun onClick(v: View) { + mBinding?.run { + var dirPath = "" + when (v.id) { + R.id.autoInstallContainer -> { + onEvent("我的光环_设置", "设置功能", "下载完成自动安装游戏") + onEvent("我的光环_设置", "自动安装游戏", if (autoInstallSwitch.isChecked) "关闭" else "打开") + autoInstallSwitch.isChecked = !autoInstallSwitch.isChecked + SPUtils.setBoolean(AUTO_INSTALL_SP_KEY, autoInstallSwitch.isChecked) + } + + R.id.concernGameContainer -> { + onEvent("我的光环_设置", "设置功能", "安装完成自动关注游戏") + onEvent("我的光环_设置", "自动关注游戏", if (concernGameSwitch.isChecked) "关闭" else "打开") + concernGameSwitch.isChecked = !concernGameSwitch.isChecked + SPUtils.setBoolean(CONCERN_GAME_SP_KEY, concernGameSwitch.isChecked) + } + + R.id.trafficContainer -> { + trafficSwitch.isChecked = !trafficSwitch.isChecked + SPUtils.setBoolean(getTrafficDownloadHintKey(), trafficSwitch.isChecked) + } + + R.id.downloadPathContainer -> { + onEvent("我的光环_设置", "设置功能", "游戏下载目录") + if (TextUtils.isEmpty(dirPath)) dirPath = Environment.getExternalStorageDirectory().path + "/gh-files" + startFilePath(dirPath) + } + + R.id.picPathContainer -> { + onEvent("我的光环_设置", "设置功能", "图片保存目录") + if (TextUtils.isEmpty(dirPath)) dirPath = Environment.getExternalStorageDirectory().path + "/pictures/ghzhushou" + startFilePath(dirPath) + } + + R.id.cleanPackageContainer -> { + onEvent("我的光环_设置", "设置功能", "安装包清理") + startActivity(CleanApkActivity.getIntent(requireContext())) + } + + else -> {} + } + } + } + + private fun startFilePath(dirPath: String) { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.setDataAndType(Uri.fromFile(File(dirPath)), "file/*") + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + try { + startActivity(intent) + } catch (e: Exception) { + toast("无法找到文件管理器") + e.printStackTrace() + } + } + + companion object { + const val AUTO_INSTALL_SP_KEY = "autoinstall" + const val CONCERN_GAME_SP_KEY = "concerngame" + const val TRAFFIC_VIDEO_SP_KEY = "tranfficvideo" + const val TRAFFIC_HOME_VIDEO_SP_KEY = "tranfficHomevideo" + const val FONT_SIZE_SP_KEY = "fontsize" + + @JvmStatic + fun getTrafficDownloadHintKey(): String { + return PackageUtils.getVersionName() + "traffic_download_hint" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/setting/VideoSettingActivity.kt b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingActivity.kt new file mode 100644 index 0000000000..dbfa662180 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingActivity.kt @@ -0,0 +1,26 @@ +package com.gh.gamecenter.setting +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.common.util.EntranceUtils +import com.gh.gamecenter.NormalActivity +import com.gh.gamecenter.R + +class VideoSettingActivity : NormalActivity() { + + companion object { + + @JvmStatic + fun getIntent(context: Context, entrance: String): Intent { + val bundle = Bundle() + bundle.putString(EntranceUtils.KEY_ENTRANCE, entrance) + return getTargetIntent(context, VideoSettingActivity::class.java, VideoSettingFragment::class.java, bundle) + } + + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setNavigationTitle(R.string.setting_game_download) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/setting/VideoSettingFragment.kt b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingFragment.kt new file mode 100644 index 0000000000..bf4994c14f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingFragment.kt @@ -0,0 +1,152 @@ +package com.gh.gamecenter.setting + +import android.os.Bundle +import android.view.View +import butterknife.OnClick +import com.gh.common.constant.Constants +import com.gh.common.util.SPUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.FragmentVideoSettingBinding +import com.gh.gamecenter.normal.NormalFragment + +class VideoSettingFragment: NormalFragment() { + + private var mBinding: FragmentVideoSettingBinding? = null + + override fun getLayoutId() = 0 + + override fun getInflatedLayout() = FragmentVideoSettingBinding.inflate(layoutInflater).apply { mBinding = this }.root + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initStatus() + } + + private fun initStatus() { + setContentVideoOption(SPUtils.getString(SP_CONTENT_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI) + setHomeOrDetailVideoOption(SPUtils.getString(SP_HOME_OR_DETAIL_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI) + mBinding?.muteSwitch?.isChecked = SPUtils.getBoolean(SP_VIDEO_PLAY_MUTE, true) + } + + private fun setContentVideoOption(status: String) { + mBinding?.run { + when (status) { + VIDEO_OPTION_ALL -> { + contentVideoOptionAllIv.visibility = View.VISIBLE + contentVideoOptionWifiIv.visibility = View.GONE + contentVideoOptionCloseIv.visibility = View.GONE + } + + VIDEO_OPTION_WIFI -> { + contentVideoOptionAllIv.visibility = View.GONE + contentVideoOptionWifiIv.visibility = View.VISIBLE + contentVideoOptionCloseIv.visibility = View.GONE + } + + VIDEO_OPTION_CLOSE -> { + contentVideoOptionAllIv.visibility = View.GONE + contentVideoOptionWifiIv.visibility = View.GONE + contentVideoOptionCloseIv.visibility = View.VISIBLE + } + } + } + } + + private fun setHomeOrDetailVideoOption(status: String) { + mBinding?.run { + when (status) { + VIDEO_OPTION_ALL -> { + homeOrDetailVideoOptionAllIv.visibility = View.VISIBLE + homeOrDetailVideoOptionWifiIv.visibility = View.GONE + homeOrDetailVideoOptionCloseIv.visibility = View.GONE + } + + VIDEO_OPTION_WIFI -> { + homeOrDetailVideoOptionAllIv.visibility = View.GONE + homeOrDetailVideoOptionWifiIv.visibility = View.VISIBLE + homeOrDetailVideoOptionCloseIv.visibility = View.GONE + } + + VIDEO_OPTION_CLOSE -> { + homeOrDetailVideoOptionAllIv.visibility = View.GONE + homeOrDetailVideoOptionWifiIv.visibility = View.GONE + homeOrDetailVideoOptionCloseIv.visibility = View.VISIBLE + } + } + } + } + + private fun isCurrentContentStatus(status: String): Boolean { + return status == SPUtils.getString(SP_CONTENT_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI + } + + private fun isCurrentHomeOrDetailStatus(status: String): Boolean { + return status == SPUtils.getString(SP_HOME_OR_DETAIL_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI + } + + @OnClick(R.id.contentVideoOptionAll, R.id.contentVideoOptionWifi, R.id.contentVideoOptionClose, + R.id.homeOrDetailVideoOptionAll, R.id.homeOrDetailVideoOptionWifi, R.id.homeOrDetailVideoOptionClose, R.id.muteContainer) + override fun onClick(v: View) { + mBinding?.run { + when (v.id) { + R.id.contentVideoOptionAll -> { + if (!isCurrentContentStatus(VIDEO_OPTION_ALL)) { + setContentVideoOption(VIDEO_OPTION_ALL) + SPUtils.setString(SP_CONTENT_VIDEO_OPTION, VIDEO_OPTION_ALL) + } + } + + R.id.contentVideoOptionWifi -> { + if (!isCurrentContentStatus(VIDEO_OPTION_WIFI)) { + setContentVideoOption(VIDEO_OPTION_WIFI) + SPUtils.setString(SP_CONTENT_VIDEO_OPTION, VIDEO_OPTION_WIFI) + } + } + + R.id.contentVideoOptionClose -> { + if (!isCurrentContentStatus(VIDEO_OPTION_CLOSE)) { + setContentVideoOption(VIDEO_OPTION_CLOSE) + SPUtils.setString(SP_CONTENT_VIDEO_OPTION, VIDEO_OPTION_CLOSE) + } + } + + R.id.homeOrDetailVideoOptionAll -> { + if (!isCurrentHomeOrDetailStatus(VIDEO_OPTION_ALL)) { + setHomeOrDetailVideoOption(VIDEO_OPTION_ALL) + SPUtils.setString(SP_HOME_OR_DETAIL_VIDEO_OPTION, VIDEO_OPTION_ALL) + } + } + + R.id.homeOrDetailVideoOptionWifi -> { + if (!isCurrentHomeOrDetailStatus(VIDEO_OPTION_WIFI)) { + setHomeOrDetailVideoOption(VIDEO_OPTION_WIFI) + SPUtils.setString(SP_HOME_OR_DETAIL_VIDEO_OPTION, VIDEO_OPTION_WIFI) + } + } + + R.id.homeOrDetailVideoOptionClose -> { + if (!isCurrentHomeOrDetailStatus(VIDEO_OPTION_CLOSE)) { + setHomeOrDetailVideoOption(VIDEO_OPTION_CLOSE) + SPUtils.setString(SP_HOME_OR_DETAIL_VIDEO_OPTION, VIDEO_OPTION_CLOSE) + } + } + + R.id.muteContainer -> { + muteSwitch.isChecked = !muteSwitch.isChecked + SPUtils.setBoolean(SP_VIDEO_PLAY_MUTE, muteSwitch.isChecked) + } + + else -> {} + } + } + } + + companion object { + const val SP_CONTENT_VIDEO_OPTION = Constants.SP_CONTENT_VIDEO_OPTION + const val SP_HOME_OR_DETAIL_VIDEO_OPTION = Constants.SP_HOME_OR_DETAIL_VIDEO_OPTION + const val SP_VIDEO_PLAY_MUTE = Constants.SP_VIDEO_PLAY_MUTE + const val VIDEO_OPTION_ALL = "video_option_all" + const val VIDEO_OPTION_WIFI = "video_option_wifi" + const val VIDEO_OPTION_CLOSE = "video_option_close" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelActivity.kt b/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelActivity.kt index 3ca37c5d5e..c2f1c2bde5 100644 --- a/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelActivity.kt @@ -3,26 +3,9 @@ package com.gh.gamecenter.video.label import android.content.Context import android.content.Intent import android.os.Bundle -import android.view.View -import android.widget.LinearLayout -import androidx.core.content.ContextCompat -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.gh.common.util.dip2px -import com.gh.common.util.viewModelProvider -import com.gh.common.view.divider.HorizontalDividerItemDecoration import com.gh.gamecenter.NormalActivity -import com.gh.gamecenter.R -import com.gh.gamecenter.baselist.ListActivity -import com.gh.gamecenter.baselist.ListAdapter -import com.gh.gamecenter.forum.follow.ForumMyFollowActivity -import com.gh.gamecenter.forum.follow.ForumMyFollowAdapter -import com.gh.gamecenter.forum.follow.ForumMyFollowFragment -import com.gh.gamecenter.normal.NormalFragment -import kotterknife.bindView +@Deprecated("v5.0.0废弃") class VideoLabelActivity : NormalActivity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelAdapter.kt b/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelAdapter.kt index 3f42798d4f..c41c6f2d25 100644 --- a/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelAdapter.kt @@ -5,18 +5,21 @@ import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.gh.base.BaseRecyclerViewHolder +import com.gh.common.util.goneIf import com.gh.gamecenter.R import com.gh.gamecenter.databinding.VideoLabelItemBinding import com.gh.gamecenter.entity.ActivityLabelEntity import com.lightgame.adapter.BaseRecyclerAdapter -class VideoLabelAdapter(context: Context, private val selectTagActivityId: String, private val onItemClickListener: (entity: ActivityLabelEntity) -> Unit) : BaseRecyclerAdapter(context) { +class VideoLabelAdapter(context: Context, private val selectTagActivityId: String) : BaseRecyclerAdapter(context) { val entityList = ArrayList() + var selectPosition = -1 fun setListData(updateData: List) { entityList.clear() entityList.addAll(updateData) + selectPosition = entityList.indexOfFirst { it.id == selectTagActivityId } notifyDataSetChanged() } @@ -29,18 +32,22 @@ class VideoLabelAdapter(context: Context, private val selectTagActivityId: Strin if (holder is VideoLabelViewHolder) { val activityLabelEntity = entityList[position] holder.binding.entity = activityLabelEntity + holder.binding.selectedIv.goneIf(selectPosition != position) holder.itemView.setOnClickListener { - onItemClickListener.invoke(activityLabelEntity) - } - if (selectTagActivityId == activityLabelEntity.id) { - holder.itemView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.bg_EFF7FF)) - } else { - holder.itemView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.white)) + selectPosition = position + notifyDataSetChanged() } } } override fun getItemCount(): Int = entityList.size + fun getSelectedActivity(): ActivityLabelEntity? { + if (selectPosition >= 0) { + return entityList[selectPosition] + } + return null + } + class VideoLabelViewHolder(val binding: VideoLabelItemBinding) : BaseRecyclerViewHolder(binding.root) } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelFragment.kt b/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelFragment.kt index f753a16cd7..a99a0bde32 100644 --- a/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/video/label/VideoLabelFragment.kt @@ -1,7 +1,5 @@ package com.gh.gamecenter.video.label -import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import android.widget.LinearLayout @@ -19,7 +17,6 @@ import com.gh.gamecenter.normal.NormalFragment import kotterknife.bindView class VideoLabelFragment : NormalFragment() { - val mRefresh by bindView(R.id.list_refresh) val mListRv by bindView(R.id.list_rv) val mReuseLoading by bindView(R.id.reuse_ll_loading) @@ -36,15 +33,16 @@ class VideoLabelFragment : NormalFragment() { mRefresh.isEnabled = false mViewModel = viewModelProvider() - val selectTagActivityId = arguments?.getString(VideoLabelActivity.TAG_ACTIVITY_ID)?:"" + val selectTagActivityId = arguments?.getString(VideoLabelActivity.TAG_ACTIVITY_ID) ?: "" mListRv.apply { layoutManager = LinearLayoutManager(requireContext()) - mAdapter = VideoLabelAdapter(requireContext(),selectTagActivityId) { + mAdapter = VideoLabelAdapter(requireContext(), selectTagActivityId) + /* { val intent = Intent() intent.putExtra(ActivityLabelEntity::class.java.simpleName, it) requireActivity().setResult(Activity.RESULT_OK, intent) requireActivity().finish() - } + }*/ val decoration = HorizontalDividerItemDecoration.Builder(requireContext()) .size(1f.dip2px()) .margin(20f.dip2px()) @@ -69,4 +67,8 @@ class VideoLabelFragment : NormalFragment() { } }) } + + fun getSelectedActivity(): ActivityLabelEntity? { + return mAdapter?.getSelectedActivity() + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterViewModel.kt b/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterViewModel.kt index ef161ce3b3..89519df8d4 100644 --- a/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterViewModel.kt @@ -28,7 +28,11 @@ class VideoPosterViewModel(application: Application, } else if (videoEntity != null) { val totalThumbsCount = 10 val startPosition = 0 - val interval = (videoEntity.length / totalThumbsCount) + val interval = if (videoEntity.length != 0L) { + videoEntity.length / totalThumbsCount + } else { + 2 + } val videoReviews = ArrayList() for (i in 0 until totalThumbsCount) { diff --git a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt index 927256cc0f..246998ead6 100644 --- a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt @@ -584,7 +584,8 @@ class UploadVideoActivity : ToolBarActivity() { } else { val localVideoPoster = application.cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg" try { - val bmp = ThumbnailUtils.createVideoThumbnail(mVideoFileEntity?.path ?: "", MediaStore.Images.Thumbnails.MINI_KIND) + val bmp = ThumbnailUtils.createVideoThumbnail(mVideoFileEntity?.path + ?: "", MediaStore.Images.Thumbnails.MINI_KIND) // bmp 可能为空 FileOutputStream(localVideoPoster).use { out -> bmp?.compress(Bitmap.CompressFormat.PNG, 100, out) @@ -612,7 +613,7 @@ class UploadVideoActivity : ToolBarActivity() { val videoDraft = mViewModel.videoDraft if (videoDraft != null) { - if (videoDraft.tagsId.contains(videoTagEntity.id)) flexCell.isChecked = true + if (videoDraft.tagsId?.contains(videoTagEntity.id) == true) flexCell.isChecked = true if (videoDraft.categoryId == videoTagEntity.id) flexCell.isChecked = true } @@ -728,7 +729,7 @@ class UploadVideoActivity : ToolBarActivity() { } } } - mVideoFileEntity = VideoFileEntity(videoPath, null, timeInSecond, videoFile.length(), format) + mVideoFileEntity = VideoFileEntity(videoPath, null, "", timeInSecond, videoFile.length(), format) createUploadTask(videoPath) } diff --git a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoViewModel.kt b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoViewModel.kt index b67c018faa..287b95788a 100644 --- a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoViewModel.kt @@ -46,7 +46,8 @@ class UploadVideoViewModel(application: Application) : AndroidViewModel(applicat init { getVideoTags() getVideoCategory() - getActivityTagStatus() + //v5.0.0关闭选择活动标签入口 + //getActivityTagStatus() } private fun postVideo(jsonObject: JSONObject) { diff --git a/app/src/main/java/com/gh/gamecenter/video/upload/view/VideoFileEntity.kt b/app/src/main/java/com/gh/gamecenter/video/upload/view/VideoFileEntity.kt index 58dc296f92..0161bb4d02 100644 --- a/app/src/main/java/com/gh/gamecenter/video/upload/view/VideoFileEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/upload/view/VideoFileEntity.kt @@ -3,7 +3,8 @@ package com.gh.gamecenter.video.upload.view data class VideoFileEntity( val path: String, var url: String?, - val length: Long, - val size: Long, + var poster: String, + var length: Long = 0, + var size: Long = 0, val format: String ) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowActivity.kt b/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftActivity.kt similarity index 57% rename from app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowActivity.kt rename to app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftActivity.kt index 76765bc2e9..91d2406bf3 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/follow/ForumMyFollowActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftActivity.kt @@ -1,20 +1,20 @@ -package com.gh.gamecenter.forum.follow +package com.gh.gamecenter.video.videomanager import android.content.Context import android.content.Intent import android.os.Bundle import com.gh.gamecenter.NormalActivity -class ForumMyFollowActivity : NormalActivity() { +class VideoDraftActivity : NormalActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setNavigationTitle("我的关注") + setNavigationTitle("视频草稿") } companion object { fun getIntent(context: Context): Intent { - return getTargetIntent(context, ForumMyFollowActivity::class.java, ForumMyFollowFragment::class.java) + return getTargetIntent(context, VideoDraftActivity::class.java, VideoDraftFragment::class.java) } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftAdapter.kt b/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftAdapter.kt index 9f0064b6d1..9908a07cfa 100644 --- a/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftAdapter.kt @@ -1,27 +1,27 @@ package com.gh.gamecenter.video.videomanager -import android.app.Activity import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView +import com.gh.base.BaseRecyclerViewHolder import com.gh.common.constant.ItemViewType import com.gh.common.util.* import com.gh.common.view.BugFixedPopupWindow import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.baselist.ListAdapter -import com.gh.gamecenter.databinding.VideoVerifyItemBinding +import com.gh.gamecenter.databinding.CommunityVideoDraftItemBinding import com.gh.gamecenter.entity.VideoDraftEntity -import com.gh.gamecenter.video.VideoVerifyItemViewHolder -import com.gh.gamecenter.video.upload.view.UploadVideoActivity class VideoDraftAdapter(context: Context, private val mViewModel: VideoDraftViewModel, - private val mEntrance: String) : ListAdapter(context) { + private val mEntrance: String, + private val selectCallback: (VideoDraftEntity) -> Unit) : ListAdapter(context) { private val mPath = "草稿箱" @@ -44,37 +44,50 @@ class VideoDraftAdapter(context: Context, FooterViewHolder(view) } else -> { - view = mLayoutInflater.inflate(R.layout.video_verify_item, parent, false) - return VideoVerifyItemViewHolder(VideoVerifyItemBinding.bind(view)) + view = mLayoutInflater.inflate(R.layout.community_video_draft_item, parent, false) + return VideoDraftViewHolder(CommunityVideoDraftItemBinding.bind(view)) } } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - if (holder is VideoVerifyItemViewHolder) { - val topPad = if (position == 0) 8F.dip2px() else 0 - val bottomPad = if (position == itemCount - 2) 8F.dip2px() else 0 - holder.itemView.setPadding(0, topPad, 0, bottomPad) - + if (holder is VideoDraftViewHolder) { val draftEntity = mEntityList[position] - ImageUtils.display(holder.binding.videoCover, draftEntity.poster) - holder.binding.videoStatus.setTextColor(R.color.theme.toColor()) - holder.binding.videoStatus.textSize = 12F - holder.binding.videoStatus.text = "继续上传" - holder.binding.videoTitle.text = if (draftEntity.title.isEmpty()) "标题为空" else draftEntity.title + holder.binding.data = draftEntity + if (draftEntity.bbsId.isNotEmpty() && draftEntity.game != null) { + holder.binding.articleDraftCommunityName.apply { + text = draftEntity.game?.name + textSize = 11f + setPadding(10f.dip2px(), 6f.dip2px(), 10f.dip2px(), 6f.dip2px()) + setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(context, R.drawable.ic_forum_label), null, null, null) + background = ContextCompat.getDrawable(context, R.drawable.bg_shape_f5_radius_999) + } + } else { + holder.binding.articleDraftCommunityName.apply { + text = "未选择论坛" + textSize = 12f + setPadding(0, 6f.dip2px(), 0, 6f.dip2px()) + setCompoundDrawablesWithIntrinsicBounds(null, null, null, null) + setBackgroundColor(ContextCompat.getColor(context, R.color.transparent)) + } + } + holder.itemView.setOnClickListener { PermissionHelper.checkStoragePermissionBeforeAction(mContext, object : EmptyCallback { override fun onCallback() { - MtaHelper.onEvent("上传视频", "视频投稿-草稿箱", "继续上传") - LogUtils.logVideoStreamingUpload("点击继续上传按钮", mPath, "", "") - val intent = UploadVideoActivity.getIntent(mContext, draftEntity, mEntrance, mPath) - (mContext as Activity).startActivityForResult(intent, VideoManagerActivity.REQUEST_CODE_DRAFT) + selectCallback.invoke(draftEntity) } }) } - holder.binding.more.setOnClickListener { - showMorePopWindow(it, draftEntity) + holder.binding.articleDraftDelete.setOnClickListener { + DialogUtils.showAlertDialog(mContext, "删除草稿", + "确定要删除视频草稿吗?删除之后不可恢复", + "确定删除", "暂时不了", + DialogUtils.ConfirmListener { + mViewModel.deleteDraft(draftEntity.id) + LogUtils.logVideoStreamingUpload("点击删除按钮", mPath, "", "") + }, null) } } else if (holder is FooterViewHolder) { holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) @@ -115,4 +128,6 @@ class VideoDraftAdapter(context: Context, override fun getItemCount(): Int { return if (mEntityList.size > 0) mEntityList.size + 1 else 0 } -} \ No newline at end of file +} + +class VideoDraftViewHolder(val binding: CommunityVideoDraftItemBinding) : BaseRecyclerViewHolder(binding.root) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftFragment.kt b/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftFragment.kt index 0e536022e8..4b47adce31 100644 --- a/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/video/videomanager/VideoDraftFragment.kt @@ -1,15 +1,22 @@ package com.gh.gamecenter.video.videomanager +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.dip2px +import com.gh.common.view.CustomDividerItemDecoration import com.gh.common.view.FixLinearLayoutManager import com.gh.common.view.GridSpacingItemDecoration +import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.entity.VideoDraftEntity +import com.gh.gamecenter.qa.draft.CommunityDraftWrapperActivity +import com.gh.gamecenter.qa.video.publish.VideoPublishActivity import com.gh.gamecenter.video.upload.view.UploadVideoActivity class VideoDraftFragment : ListFragment() { @@ -25,12 +32,24 @@ class VideoDraftFragment : ListFragment() } override fun getItemDecoration(): RecyclerView.ItemDecoration { - return GridSpacingItemDecoration(1, 0, false, 8F.dip2px()) + val insetDivider = ContextCompat.getDrawable(requireContext(), R.drawable.divider_item_line_space_16) + val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheLastItem = true) + itemDecoration.setDrawable(insetDivider!!) + return itemDecoration } override fun provideListAdapter(): VideoDraftAdapter { if (mAdapter == null) { - mAdapter = VideoDraftAdapter(requireContext(), mListViewModel, mEntrance) + mAdapter = VideoDraftAdapter(requireContext(), mListViewModel, mEntrance) { + if (requireActivity() is CommunityDraftWrapperActivity) { + startActivity(VideoPublishActivity.getDraftIntent(requireContext(), it, mEntrance, "视频草稿")) + } else { + val intent = Intent() + intent.putExtra(VideoDraftEntity::class.java.simpleName, it) + requireActivity().setResult(Activity.RESULT_OK, intent) + requireActivity().finish() + } + } } return mAdapter!! } diff --git a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java index 55157de8e9..2d5e098974 100644 --- a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.java @@ -4,18 +4,14 @@ import android.app.Dialog; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; -import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.preference.PreferenceManager; import android.provider.Settings; -import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.RadioButton; import android.widget.RadioGroup; -import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.Nullable; @@ -35,12 +31,10 @@ import com.gh.common.util.NotificationHelper; import com.gh.common.util.PackageUtils; import com.gh.common.util.PermissionHelper; import com.gh.common.util.SPUtils; -import com.gh.common.util.StringUtils; import com.gh.common.util.UsageStatsHelper; import com.gh.download.server.BrowserInstallHelper; import com.gh.gamecenter.AboutActivity; import com.gh.gamecenter.BuildConfig; -import com.gh.gamecenter.CleanApkActivity; import com.gh.gamecenter.MainActivity; import com.gh.gamecenter.NetworkDiagnosisActivity; import com.gh.gamecenter.R; @@ -53,11 +47,13 @@ import com.gh.gamecenter.game.upload.GameSubmissionActivity; import com.gh.gamecenter.manager.UserManager; import com.gh.gamecenter.normal.NormalFragment; import com.gh.gamecenter.security.SecurityActivity; +import com.gh.gamecenter.setting.GameDownloadSettingActivity; +import com.gh.gamecenter.setting.VideoSettingActivity; import com.gh.gamecenter.user.UserRepository; import com.halo.assistant.HaloApp; -import com.kyleduo.switchbutton.SwitchButton; import com.lightgame.download.FileUtils; import com.lightgame.utils.Utils; +import com.lightgame.view.CheckableImageView; import org.greenrobot.eventbus.EventBus; @@ -79,30 +75,16 @@ import static java.lang.Thread.sleep; */ public class SettingsFragment extends NormalFragment { - @BindView(R.id.setting_sb_autoinstall) - SwitchButton mSettingAutoinstallSb; - @BindView(R.id.setting_sb_concerngame) - SwitchButton mSettingConcerngameSb; - @BindView(R.id.setting_sb_usage_stats) - SwitchButton mSettingUsageStatsSb; - @BindView(R.id.setting_sb_traffic_video) - SwitchButton mSettingTrafficVideoSb; - @BindView(R.id.setting_sb_traffic) - SwitchButton mSettingTrafficSb; - @BindView(R.id.setting_sb_home_video) - SwitchButton mSettingHomeVideoSb; + @BindView(R.id.usageStatsSwitch) + CheckableImageView mSettingUsageStatsSb; + @BindView(R.id.notificationAuthoritySwitch) + CheckableImageView mSettingNotificationAuthoritySb; @BindView(R.id.setting_tv_cache) TextView mSettingCacheTv; - @BindView(R.id.setting_logout_tv) - TextView mSettingLoginType; - @BindView(R.id.setting_logout_rl) - RelativeLayout mSettingLoginRl; + @BindView(R.id.setting_logout) + TextView mSettingLogout; @BindView(R.id.setting_rl_about_version) TextView mVersionName; - @BindView(R.id.setting_notification_authority_tv) - TextView mNotificationAuthorityTv; - @BindView(R.id.setting_notification_authority_tips) - TextView mNotificationAuthorityTipsTv; @BindView(R.id.privacy_red_dot) View mPrivacyRedDotView; @BindView(R.id.setting_rl_install_method) @@ -113,7 +95,6 @@ public class SettingsFragment extends NormalFragment { public static final String AUTO_INSTALL_SP_KEY = "autoinstall"; public static final String CONCERN_GAME_SP_KEY = "concerngame"; public static final String TRAFFIC_VIDEO_SP_KEY = "tranfficvideo"; - public static final String TRAFFIC_HOME_VIDEO_SP_KEY = "tranfficHomevideo"; public static final String FONT_SIZE_SP_KEY = "fontsize"; public static final int INSERT_MOBILE_CODE = 411; @@ -126,10 +107,6 @@ public class SettingsFragment extends NormalFragment { private boolean mIsUpdate; - public static String getTrafficDownloadHintKey() { - return PackageUtils.getVersionName() + "traffic_download_hint"; - } - @Override public void onStop() { saveCurrentSetting(); @@ -189,14 +166,14 @@ public class SettingsFragment extends NormalFragment { mPrivacyRedDotView.setVisibility(checkPrivacyIsSame() ? View.GONE : View.VISIBLE); mSettingCacheTv.setText(getCacheSize()); - mSettingAutoinstallSb.setCheckedImmediately(sp.getBoolean(AUTO_INSTALL_SP_KEY, true)); - mSettingConcerngameSb.setCheckedImmediately(sp.getBoolean(CONCERN_GAME_SP_KEY, true)); - mSettingTrafficVideoSb.setCheckedImmediately(sp.getBoolean(TRAFFIC_VIDEO_SP_KEY, false)); - mSettingHomeVideoSb.setCheckedImmediately(sp.getBoolean(TRAFFIC_HOME_VIDEO_SP_KEY, false)); - mSettingTrafficSb.setCheckedImmediately(sp.getBoolean(getTrafficDownloadHintKey(), true)); - mSettingTrafficSb.setOnCheckedChangeListener((buttonView, isChecked) -> { - MtaHelper.onEvent("我的光环_设置", "流量下载提醒", isChecked ? "打开" : "关闭"); - }); +// mSettingAutoinstallSb.setCheckedImmediately(sp.getBoolean(AUTO_INSTALL_SP_KEY, true)); +// mSettingConcerngameSb.setCheckedImmediately(sp.getBoolean(CONCERN_GAME_SP_KEY, true)); +// mSettingTrafficVideoSb.setCheckedImmediately(sp.getBoolean(TRAFFIC_VIDEO_SP_KEY, false)); +// mSettingHomeVideoSb.setCheckedImmediately(sp.getBoolean(TRAFFIC_HOME_VIDEO_SP_KEY, false)); +// mSettingTrafficSb.setCheckedImmediately(sp.getBoolean(getTrafficDownloadHintKey(), true)); +// mSettingTrafficSb.setOnCheckedChangeListener((buttonView, isChecked) -> { +// MtaHelper.onEvent("我的光环_设置", "流量下载提醒", isChecked ? "打开" : "关闭"); +// }); checkSizeIndex = sp.getInt(FONT_SIZE_SP_KEY, 1); if (checkSizeIndex == 0) { @@ -220,15 +197,9 @@ public class SettingsFragment extends NormalFragment { super.onResume(); boolean notificationIsEnable = NotificationHelper.notificationIsEnable(); if (notificationIsEnable) { - mNotificationAuthorityTipsTv.setVisibility(View.GONE); - mNotificationAuthorityTv.setText("已开启"); - mNotificationAuthorityTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_cccccc)); - mNotificationAuthorityTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + mSettingNotificationAuthoritySb.setChecked(true); } else { - mNotificationAuthorityTipsTv.setVisibility(View.VISIBLE); - mNotificationAuthorityTv.setText("去开启"); - mNotificationAuthorityTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.title)); - mNotificationAuthorityTv.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(requireContext(), R.drawable.answer_detail_more_answer_icon), null); + mSettingNotificationAuthoritySb.setChecked(false); } if (BrowserInstallHelper.isUseBrowserToInstallEnabled()) { @@ -254,76 +225,33 @@ public class SettingsFragment extends NormalFragment { mSettingUsageStatsSb.setClickable(false); if (UsageStatsHelper.checkForPermission() && sp.getBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, true)) { - mSettingUsageStatsSb.setCheckedImmediately(true); + mSettingUsageStatsSb.setChecked(true); EnergyTaskHelper.postEnergyTask("open_game_time"); } - mSettingUsageStatsSb.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (isChecked) { - if (UsageStatsHelper.checkForPermission()) { - sp.edit().putBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, true).apply(); - EnergyTaskHelper.postEnergyTask("open_game_time"); - } else { - DialogUtils.showUsageStatsDialog(getContext(), - () -> { - UsageStatsHelper.skipToUsageStats(requireContext(), UsageStatsHelper.USAGE_STATUS_REQUEST_CODE); - mSettingUsageStatsSb.setCheckedNoEvent(false); - }, - () -> mSettingUsageStatsSb.setCheckedNoEvent(false)); - } - } - }); +// mSettingUsageStatsSb.setOnCheckedChangeListener((buttonView, isChecked) -> { +// if (isChecked) { +// if (UsageStatsHelper.checkForPermission()) { +// sp.edit().putBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, true).apply(); +// EnergyTaskHelper.postEnergyTask("open_game_time"); +// } else { +// DialogUtils.showUsageStatsDialog(getContext(), +// () -> { +// UsageStatsHelper.skipToUsageStats(requireContext(), UsageStatsHelper.USAGE_STATUS_REQUEST_CODE); +// mSettingUsageStatsSb.setCheckedNoEvent(false); +// }, +// () -> mSettingUsageStatsSb.setCheckedNoEvent(false)); +// } +// } +// }); } private void initLoginStatus() { LoginTokenEntity loginToken = UserManager.getInstance().getLoginTokenEntity(); if (loginToken != null) { - if (!TextUtils.isEmpty(loginToken.getLoginType())) { - String loginType = loginToken.getLoginType().trim(); - switch (loginType) { - case "qq": - loginType = "QQ"; - break; - case "wechat": - loginType = "微信"; - break; - case "weibo": - loginType = "新浪微博"; - break; - case "douyin": - loginType = "抖音"; - break; - case "oauth": - if (UserManager.getInstance().getUserInfoEntity() != null) { - loginType = UserManager.getInstance().getUserInfoEntity().getLoginMobile(); - if (loginType != null) { - if (loginType.length() == 11) { - String sub1 = loginType.substring(0, 3); - String sub2 = loginType.substring(9, 11); - loginType = StringUtils.buildString(sub1, "******", sub2); - } else { - loginType = "一键登录"; - } - } else { - loginType = "一键登录"; - } - } else { - loginType = "一键登录"; - } - break; - default: - if (loginType.length() == 11) { - String sub1 = loginType.substring(0, 3); - String sub2 = loginType.substring(9, 11); - loginType = StringUtils.buildString(sub1, "******", sub2); - } - break; - } - mSettingLoginType.setText(loginType); - } - mSettingLoginRl.setVisibility(View.VISIBLE); + mSettingLogout.setVisibility(View.VISIBLE); } else { - mSettingLoginRl.setVisibility(View.GONE); + mSettingLogout.setVisibility(View.GONE); } } @@ -335,12 +263,6 @@ public class SettingsFragment extends NormalFragment { private void saveCurrentSetting() { SharedPreferences.Editor mEditor = sp.edit(); - mEditor.putBoolean(AUTO_INSTALL_SP_KEY, mSettingAutoinstallSb.isChecked()); -// mEditor.putBoolean("autodelete", mSettingAutodeleteSb.isChecked()); - mEditor.putBoolean(CONCERN_GAME_SP_KEY, mSettingConcerngameSb.isChecked()); - mEditor.putBoolean(TRAFFIC_VIDEO_SP_KEY, mSettingTrafficVideoSb.isChecked()); - mEditor.putBoolean(TRAFFIC_HOME_VIDEO_SP_KEY, mSettingHomeVideoSb.isChecked()); - mEditor.putBoolean(getTrafficDownloadHintKey(), mSettingTrafficSb.isChecked()); mEditor.putInt(FONT_SIZE_SP_KEY, checkSizeIndex); mEditor.apply(); } @@ -385,20 +307,24 @@ public class SettingsFragment extends NormalFragment { } @OnClick({ - R.id.setting_rl_security, - R.id.setting_cv_fix, R.id.setting_rl_autoinstall, - R.id.setting_rl_cache, R.id.setting_rl_traffic_video, - R.id.setting_rl_concerngame, R.id.setting_rl_about, - R.id.setting_logout_rl, R.id.setting_network_diagnosis, + R.id.setting_rl_security, R.id.setting_cv_fix, + R.id.setting_rl_cache, R.id.setting_rl_about, + R.id.setting_logout, R.id.setting_network_diagnosis, R.id.setting_privacy_policy, R.id.setting_user_protocol, R.id.setting_game_submission, R.id.setting_usage_stats, - R.id.setting_clean_package, R.id.setting_rl_traffic, - R.id.setting_notification_authority, R.id.setting_rl_home_video + R.id.setting_notification_authority, R.id.setting_game_download, + R.id.setting_video }) @Override public void onClick(final View v) { - String dirPath = null; +// String dirPath = null; switch (v.getId()) { + case R.id.setting_game_download: + startActivity(GameDownloadSettingActivity.getIntent(requireContext(), "设置")); + break; + case R.id.setting_video: + startActivity(VideoSettingActivity.getIntent(requireContext(), "设置")); + break; case R.id.setting_rl_security: MtaHelper.onEvent("我的光环_设置", "设置功能", "账号与安全"); CheckLoginUtils.checkLogin(getContext(), "设置-账号与安全-请先登录", () -> { @@ -412,11 +338,11 @@ public class SettingsFragment extends NormalFragment { EventBus.getDefault().post(new EBReuse("Refresh")); MainActivity.skipToMainActivity(getContext(), MainWrapperFragment.INDEX_HOME); break; - case R.id.setting_rl_autoinstall: - MtaHelper.onEvent("我的光环_设置", "设置功能", "下载完成自动安装游戏"); - MtaHelper.onEvent("我的光环_设置", "自动安装游戏", mSettingAutoinstallSb.isChecked() ? "关闭" : "打开"); - mSettingAutoinstallSb.performClick(); - break; +// case R.id.setting_rl_autoinstall: +// MtaHelper.onEvent("我的光环_设置", "设置功能", "下载完成自动安装游戏"); +// MtaHelper.onEvent("我的光环_设置", "自动安装游戏", mSettingAutoinstallSb.isChecked() ? "关闭" : "打开"); +// mSettingAutoinstallSb.performClick(); +// break; case R.id.setting_rl_cache: MtaHelper.onEvent("我的光环_设置", "设置功能", "清除缓存"); DialogUtils.showWarningDialog(getContext(), "清除缓存", "确定要清除缓存吗", @@ -434,12 +360,12 @@ public class SettingsFragment extends NormalFragment { MtaHelper.onEvent("我的光环_设置", "设置功能", "关于"); startActivity(AboutActivity.getIntent(getContext(), mIsUpdate)); break; - case R.id.setting_rl_concerngame: - MtaHelper.onEvent("我的光环_设置", "设置功能", "安装完成自动关注游戏"); - MtaHelper.onEvent("我的光环_设置", "自动关注游戏", mSettingConcerngameSb.isChecked() ? "关闭" : "打开"); - mSettingConcerngameSb.performClick(); - break; - case R.id.setting_logout_rl: +// case R.id.setting_rl_concerngame: +// MtaHelper.onEvent("我的光环_设置", "设置功能", "安装完成自动关注游戏"); +// MtaHelper.onEvent("我的光环_设置", "自动关注游戏", mSettingConcerngameSb.isChecked() ? "关闭" : "打开"); +// mSettingConcerngameSb.performClick(); +// break; + case R.id.setting_logout: MtaHelper.onEvent("我的光环_设置", "设置功能", "退出账号"); DialogUtils.showForceDialog(getContext(), "退出登录", "退出账号即会回到游客状态,很多功能将无法使用(例如评论、客服消息),确定退出吗?", "确定退出", "取消", () -> { @@ -451,25 +377,26 @@ public class SettingsFragment extends NormalFragment { }); }, null); break; - case R.id.setting_rl_pic_path: - MtaHelper.onEvent("我的光环_设置", "设置功能", "图片保存目录"); - if (TextUtils.isEmpty(dirPath)) - dirPath = Environment.getExternalStorageDirectory().getPath() + "/pictures/ghzhushou"; - case R.id.setting_rl_download_path: - MtaHelper.onEvent("我的光环_设置", "设置功能", "游戏下载目录"); - if (TextUtils.isEmpty(dirPath)) - dirPath = Environment.getExternalStorageDirectory().getPath() + "/gh-files"; - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.setDataAndType(Uri.fromFile(new File(dirPath)), "file/*"); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - startActivity(intent); - } catch (Exception e) { - toast("无法找到文件管理器"); - e.printStackTrace(); - } - break; +// case R.id.setting_rl_pic_path: +// MtaHelper.onEvent("我的光环_设置", "设置功能", "图片保存目录"); +// if (TextUtils.isEmpty(dirPath)) +// dirPath = Environment.getExternalStorageDirectory().getPath() + "/pictures/ghzhushou"; +// break; +// case R.id.setting_rl_download_path: +// MtaHelper.onEvent("我的光环_设置", "设置功能", "游戏下载目录"); +// if (TextUtils.isEmpty(dirPath)) +// dirPath = Environment.getExternalStorageDirectory().getPath() + "/gh-files"; +// Intent intent = new Intent(Intent.ACTION_GET_CONTENT); +// intent.setDataAndType(Uri.fromFile(new File(dirPath)), "file/*"); +// intent.addCategory(Intent.CATEGORY_OPENABLE); +// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +// try { +// startActivity(intent); +// } catch (Exception e) { +// toast("无法找到文件管理器"); +// e.printStackTrace(); +// } +// break; case R.id.setting_network_diagnosis: MtaHelper.onEvent("我的光环_设置", "设置功能", "网络诊断"); startActivity(new Intent(getContext(), NetworkDiagnosisActivity.class)); @@ -503,27 +430,40 @@ public class SettingsFragment extends NormalFragment { mSettingUsageStatsSb.setChecked(false); sp.edit().putBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, false).apply(); }, - () -> mSettingUsageStatsSb.setCheckedNoEvent(true) + () -> mSettingUsageStatsSb.setChecked(true) ); } else { - mSettingUsageStatsSb.performClick(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + if (UsageStatsHelper.checkForPermission()) { + mSettingUsageStatsSb.setChecked(true); + sp.edit().putBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, true).apply(); + EnergyTaskHelper.postEnergyTask("open_game_time"); + } else { + DialogUtils.showUsageStatsDialog(getContext(), + () -> { + UsageStatsHelper.skipToUsageStats(requireContext(), UsageStatsHelper.USAGE_STATUS_REQUEST_CODE); + mSettingUsageStatsSb.setChecked(false); + }, + () -> mSettingUsageStatsSb.setChecked(false)); + } + } } break; - case R.id.setting_clean_package: -// MtaHelper.onEvent("发现", "安装包清理"); -// DataCollectionUtils.uploadClick(requireContext(), "安装包清理", "发现"); - MtaHelper.onEvent("我的光环_设置", "设置功能", "安装包清理"); - startActivity(CleanApkActivity.getIntent(requireContext())); - break; - case R.id.setting_rl_traffic: - mSettingTrafficSb.performClick(); - break; - case R.id.setting_rl_traffic_video: - mSettingTrafficVideoSb.performClick(); - break; +// case R.id.setting_clean_package: +//// MtaHelper.onEvent("发现", "安装包清理"); +//// DataCollectionUtils.uploadClick(requireContext(), "安装包清理", "发现"); +// MtaHelper.onEvent("我的光环_设置", "设置功能", "安装包清理"); +// startActivity(CleanApkActivity.getIntent(requireContext())); +// break; +// case R.id.setting_rl_traffic: +// mSettingTrafficSb.performClick(); +// break; +// case R.id.setting_rl_traffic_video: +// mSettingTrafficVideoSb.performClick(); +// break; case R.id.setting_notification_authority: - boolean notificationIsEnable = NotificationHelper.notificationIsEnable(); - if (notificationIsEnable) return; +// boolean notificationIsEnable = NotificationHelper.notificationIsEnable(); +// if (notificationIsEnable) return; MtaHelper.onEvent("我的光环_设置", "设置功能", "通知权限"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //这种方案适用于 API 26, 即8.0(含8.0)以上可以用 @@ -535,9 +475,9 @@ public class SettingsFragment extends NormalFragment { PermissionHelper.INSTANCE.toPermissionSetting(requireActivity()); } break; - case R.id.setting_rl_home_video: - mSettingHomeVideoSb.performClick(); - break; +// case R.id.setting_rl_home_video: +// mSettingHomeVideoSb.performClick(); +// break; default: break; } diff --git a/app/src/main/res/anim/community_publication_enter.xml b/app/src/main/res/anim/community_publication_enter.xml index 271e682f8e..d8ae968389 100644 --- a/app/src/main/res/anim/community_publication_enter.xml +++ b/app/src/main/res/anim/community_publication_enter.xml @@ -2,6 +2,6 @@ diff --git a/app/src/main/res/color/forum_category_selector.xml b/app/src/main/res/color/forum_category_selector.xml new file mode 100644 index 0000000000..fafdf93fce --- /dev/null +++ b/app/src/main/res/color/forum_category_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xxhdpi/bg_forum_home_top.png b/app/src/main/res/drawable-xxhdpi/bg_forum_home_top.png new file mode 100644 index 0000000000..3c51bf8186 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_forum_home_top.png differ diff --git a/app/src/main/res/drawable-xxhdpi/community_edit_article.webp b/app/src/main/res/drawable-xxhdpi/community_edit_article.webp index 05d8cfd60d..5fb8624587 100644 Binary files a/app/src/main/res/drawable-xxhdpi/community_edit_article.webp and b/app/src/main/res/drawable-xxhdpi/community_edit_article.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/community_edit_close.png b/app/src/main/res/drawable-xxhdpi/community_edit_close.png new file mode 100644 index 0000000000..8383082598 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/community_edit_close.png differ diff --git a/app/src/main/res/drawable-xxhdpi/community_edit_close.webp b/app/src/main/res/drawable-xxhdpi/community_edit_close.webp deleted file mode 100644 index 2bd31942fc..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/community_edit_close.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/community_edit_icon.webp b/app/src/main/res/drawable-xxhdpi/community_edit_icon.webp deleted file mode 100644 index 55f98b4ffb..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/community_edit_icon.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/community_edit_question.webp b/app/src/main/res/drawable-xxhdpi/community_edit_question.webp index d312d27892..c093c8cfa8 100644 Binary files a/app/src/main/res/drawable-xxhdpi/community_edit_question.webp and b/app/src/main/res/drawable-xxhdpi/community_edit_question.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/community_edit_video.webp b/app/src/main/res/drawable-xxhdpi/community_edit_video.webp new file mode 100644 index 0000000000..2a7459bff8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/community_edit_video.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_change_bg_tips.png b/app/src/main/res/drawable-xxhdpi/ic_change_bg_tips.png new file mode 100644 index 0000000000..a8950b38b5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_change_bg_tips.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_choose_activity.webp b/app/src/main/res/drawable-xxhdpi/ic_choose_activity.webp new file mode 100644 index 0000000000..38af24a83d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_choose_activity.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_choose_media_bg.png b/app/src/main/res/drawable-xxhdpi/ic_choose_media_bg.png new file mode 100644 index 0000000000..24e3617165 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_choose_media_bg.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_choose_media_normal.png b/app/src/main/res/drawable-xxhdpi/ic_choose_media_normal.png new file mode 100644 index 0000000000..6e53e916c9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_choose_media_normal.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_choose_media_selected.png b/app/src/main/res/drawable-xxhdpi/ic_choose_media_selected.png new file mode 100644 index 0000000000..db2e365d12 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_choose_media_selected.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_community_comment_vertical_more.png b/app/src/main/res/drawable-xxhdpi/ic_community_comment_vertical_more.png new file mode 100644 index 0000000000..615f97b257 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_community_comment_vertical_more.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_del_video.png b/app/src/main/res/drawable-xxhdpi/ic_del_video.png new file mode 100644 index 0000000000..f37d1eeb97 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_del_video.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_detail_game_zone_left.webp b/app/src/main/res/drawable-xxhdpi/ic_forum_detail_game_zone_left.webp deleted file mode 100644 index 90014f25aa..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_forum_detail_game_zone_left.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_detail_game_zone_right.png b/app/src/main/res/drawable-xxhdpi/ic_forum_detail_game_zone_right.png deleted file mode 100644 index 4bd0983d15..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_forum_detail_game_zone_right.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_arrow.png b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_arrow.png new file mode 100644 index 0000000000..0b26c6fa41 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_arrow.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_collect.webp b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_collect.webp new file mode 100644 index 0000000000..1d6845468c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_collect.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_collected.webp b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_collected.webp new file mode 100644 index 0000000000..5de9bf1a1a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_collected.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_like.png b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_like.png new file mode 100644 index 0000000000..fb7ac78925 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_like.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_liked.webp b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_liked.webp new file mode 100644 index 0000000000..d09da0b26b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_liked.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_play.webp b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_play.webp new file mode 100644 index 0000000000..aa6e382fc8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_play.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_share.png b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_share.png new file mode 100644 index 0000000000..889255ae46 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_forum_video_detail_share.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_gif_label.webp b/app/src/main/res/drawable-xxhdpi/ic_gif_label.webp deleted file mode 100644 index fa9a621f06..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_gif_label.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_question_detail_invite.webp b/app/src/main/res/drawable-xxhdpi/ic_question_detail_invite.webp new file mode 100644 index 0000000000..27308c39d4 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_question_detail_invite.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_video_replay.png b/app/src/main/res/drawable-xxhdpi/ic_video_replay.png new file mode 100644 index 0000000000..728495493a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_video_replay.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_video_share.png b/app/src/main/res/drawable-xxhdpi/ic_video_share.png new file mode 100644 index 0000000000..2aeaadd1f6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_video_share.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_more.webp b/app/src/main/res/drawable-xxhdpi/icon_more.webp deleted file mode 100644 index 182bd37b9a..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/icon_more.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_preview_video_pause.webp b/app/src/main/res/drawable-xxhdpi/icon_preview_video_pause.webp new file mode 100644 index 0000000000..67c3811514 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_preview_video_pause.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_preview_video_play.webp b/app/src/main/res/drawable-xxhdpi/icon_preview_video_play.webp new file mode 100644 index 0000000000..b8d47a3996 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_preview_video_play.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_upload_video_btn.png b/app/src/main/res/drawable-xxhdpi/icon_upload_video_btn.png new file mode 100644 index 0000000000..424d1c5b08 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_upload_video_btn.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_forum_activity_status_blue.webp b/app/src/main/res/drawable-xxxhdpi/bg_forum_activity_status_blue.webp new file mode 100644 index 0000000000..29486af370 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_forum_activity_status_blue.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_forum_activity_status_gray.webp b/app/src/main/res/drawable-xxxhdpi/bg_forum_activity_status_gray.webp new file mode 100644 index 0000000000..f57479b90f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_forum_activity_status_gray.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_forum_home_search.webp b/app/src/main/res/drawable-xxxhdpi/bg_forum_home_search.webp new file mode 100644 index 0000000000..6c02f14862 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_forum_home_search.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/community_edit_icon.webp b/app/src/main/res/drawable-xxxhdpi/community_edit_icon.webp new file mode 100644 index 0000000000..58ae8dd724 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/community_edit_icon.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_article_video_full_screen.webp b/app/src/main/res/drawable-xxxhdpi/ic_article_video_full_screen.webp new file mode 100644 index 0000000000..5dbb2888ad Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_article_video_full_screen.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_article_video_volume_off.webp b/app/src/main/res/drawable-xxxhdpi/ic_article_video_volume_off.webp new file mode 100644 index 0000000000..136ceebc09 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_article_video_volume_off.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_article_video_volume_on.webp b/app/src/main/res/drawable-xxxhdpi/ic_article_video_volume_on.webp new file mode 100644 index 0000000000..7250221800 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_article_video_volume_on.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_accelerator.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_accelerator.webp new file mode 100644 index 0000000000..aadb563093 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_accelerator.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_detail_game_zone_left.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_detail_game_zone_left.webp new file mode 100644 index 0000000000..b1472c908f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_detail_game_zone_left.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_detail_game_zone_right.png b/app/src/main/res/drawable-xxxhdpi/ic_forum_detail_game_zone_right.png new file mode 100644 index 0000000000..b769e36ae4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_detail_game_zone_right.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_game_moment.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_game_moment.webp new file mode 100644 index 0000000000..992d3d0d12 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_game_moment.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_libao_center.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_libao_center.webp new file mode 100644 index 0000000000..0a63fec4ed Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_libao_center.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_news.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_news.webp new file mode 100644 index 0000000000..44ff7586fc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_news.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_rule.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_rule.webp new file mode 100644 index 0000000000..387da92dd3 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_rule.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_tab_indicator.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_tab_indicator.webp new file mode 100644 index 0000000000..6e28266024 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_tab_indicator.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_forum_tool_box.webp b/app/src/main/res/drawable-xxxhdpi/ic_forum_tool_box.webp new file mode 100644 index 0000000000..8d345a994c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_forum_tool_box.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_gif_label.webp b/app/src/main/res/drawable-xxxhdpi/ic_gif_label.webp new file mode 100644 index 0000000000..944ce18990 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_gif_label.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_select_forum.webp b/app/src/main/res/drawable-xxxhdpi/ic_select_forum.webp new file mode 100644 index 0000000000..2a92912a54 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_select_forum.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_setting_switch_off.webp b/app/src/main/res/drawable-xxxhdpi/ic_setting_switch_off.webp new file mode 100644 index 0000000000..1989b45f36 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_setting_switch_off.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_setting_switch_on.webp b/app/src/main/res/drawable-xxxhdpi/ic_setting_switch_on.webp new file mode 100644 index 0000000000..e9345372ea Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_setting_switch_on.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_user_commenr_arrow_down.png b/app/src/main/res/drawable-xxxhdpi/ic_user_commenr_arrow_down.png new file mode 100644 index 0000000000..4453ae5e66 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_user_commenr_arrow_down.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_video_setting_select.webp b/app/src/main/res/drawable-xxxhdpi/ic_video_setting_select.webp new file mode 100644 index 0000000000..93d93da5e5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_video_setting_select.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/skeleton_forum_video_detail.webp b/app/src/main/res/drawable-xxxhdpi/skeleton_forum_video_detail.webp new file mode 100644 index 0000000000..8e235997d1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/skeleton_forum_video_detail.webp differ diff --git a/app/src/main/res/drawable/background_shape_round_stroke_theme_font.xml b/app/src/main/res/drawable/background_shape_round_stroke_theme_font.xml new file mode 100644 index 0000000000..77165152e0 --- /dev/null +++ b/app/src/main/res/drawable/background_shape_round_stroke_theme_font.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_change_cover_btn.xml b/app/src/main/res/drawable/bg_change_cover_btn.xml new file mode 100644 index 0000000000..1d2e5fd0e9 --- /dev/null +++ b/app/src/main/res/drawable/bg_change_cover_btn.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_choose_forum.xml b/app/src/main/res/drawable/bg_choose_forum.xml index c95fbd1861..9281c5f911 100644 --- a/app/src/main/res/drawable/bg_choose_forum.xml +++ b/app/src/main/res/drawable/bg_choose_forum.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_category_selected.xml b/app/src/main/res/drawable/bg_forum_category_selected.xml new file mode 100644 index 0000000000..0ab8d3be01 --- /dev/null +++ b/app/src/main/res/drawable/bg_forum_category_selected.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_category_unselect.xml b/app/src/main/res/drawable/bg_forum_category_unselect.xml new file mode 100644 index 0000000000..834a7cf6aa --- /dev/null +++ b/app/src/main/res/drawable/bg_forum_category_unselect.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_game_zone.xml b/app/src/main/res/drawable/bg_forum_game_zone.xml index 05ba39c436..4d45e86ffc 100644 --- a/app/src/main/res/drawable/bg_forum_game_zone.xml +++ b/app/src/main/res/drawable/bg_forum_game_zone.xml @@ -1,8 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_rule.xml b/app/src/main/res/drawable/bg_forum_rule.xml new file mode 100644 index 0000000000..6b31380455 --- /dev/null +++ b/app/src/main/res/drawable/bg_forum_rule.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_video_detail_shadow.xml b/app/src/main/res/drawable/bg_forum_video_detail_shadow.xml new file mode 100644 index 0000000000..4618e8ecb9 --- /dev/null +++ b/app/src/main/res/drawable/bg_forum_video_detail_shadow.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_pic_count_label.xml b/app/src/main/res/drawable/bg_pic_count_label.xml index d9f866721c..479ccf577d 100644 --- a/app/src/main/res/drawable/bg_pic_count_label.xml +++ b/app/src/main/res/drawable/bg_pic_count_label.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_user_comment_game_info.xml b/app/src/main/res/drawable/bg_user_comment_game_info.xml new file mode 100644 index 0000000000..4ff2294990 --- /dev/null +++ b/app/src/main/res/drawable/bg_user_comment_game_info.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/border_round_stroke_0dot5_eee_999.xml b/app/src/main/res/drawable/border_round_stroke_0dot5_eee_999.xml new file mode 100644 index 0000000000..2e24f2006c --- /dev/null +++ b/app/src/main/res/drawable/border_round_stroke_0dot5_eee_999.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_edf5fc.xml b/app/src/main/res/drawable/button_round_edf5fc.xml new file mode 100644 index 0000000000..6dc8c0acdf --- /dev/null +++ b/app/src/main/res/drawable/button_round_edf5fc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_f0f8fa.xml b/app/src/main/res/drawable/button_round_f0f8fa.xml new file mode 100644 index 0000000000..92752d1151 --- /dev/null +++ b/app/src/main/res/drawable/button_round_f0f8fa.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_f0f8ff.xml b/app/src/main/res/drawable/button_round_f0f8ff.xml new file mode 100644 index 0000000000..54f327c352 --- /dev/null +++ b/app/src/main/res/drawable/button_round_f0f8ff.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_fafafa.xml b/app/src/main/res/drawable/button_round_fafafa.xml new file mode 100644 index 0000000000..af3cb9c3d7 --- /dev/null +++ b/app/src/main/res/drawable/button_round_fafafa.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/forum_detail_mask_default.xml b/app/src/main/res/drawable/forum_detail_mask_default.xml new file mode 100644 index 0000000000..f3c8a5306c --- /dev/null +++ b/app/src/main/res/drawable/forum_detail_mask_default.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/oval_forum_unread_bg.xml b/app/src/main/res/drawable/oval_forum_unread_bg.xml new file mode 100644 index 0000000000..6c5bf57d90 --- /dev/null +++ b/app/src/main/res/drawable/oval_forum_unread_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_bg_forum_activity_category.xml b/app/src/main/res/drawable/selector_bg_forum_activity_category.xml new file mode 100644 index 0000000000..70b14ba06f --- /dev/null +++ b/app/src/main/res/drawable/selector_bg_forum_activity_category.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_ic_setting_switch.xml b/app/src/main/res/drawable/selector_ic_setting_switch.xml new file mode 100644 index 0000000000..03a4068394 --- /dev/null +++ b/app/src/main/res/drawable/selector_ic_setting_switch.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_community_article_edit.xml b/app/src/main/res/layout/activity_community_article_edit.xml index 20d8aa49c7..96daa6b4f8 100644 --- a/app/src/main/res/layout/activity_community_article_edit.xml +++ b/app/src/main/res/layout/activity_community_article_edit.xml @@ -46,36 +46,67 @@ + android:orientation="horizontal" + android:paddingLeft="16dp" + android:paddingRight="16dp"> - + + - + android:drawableRight="@drawable/ic_forum_arrow_right" + android:drawablePadding="4dp" + android:ellipsize="end" + android:gravity="end" + android:hint="添加活动标签" + android:includeFontPadding="false" + android:maxLines="1" + android:textColor="@color/text_333333" + android:textColorHint="@color/text_999999" + android:textSize="12sp" /> + + + + diff --git a/app/src/main/res/layout/activity_editor_video.xml b/app/src/main/res/layout/activity_editor_video.xml deleted file mode 100644 index fbf97bbd5c..0000000000 --- a/app/src/main/res/layout/activity_editor_video.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_full_screen_video.xml b/app/src/main/res/layout/activity_full_screen_video.xml new file mode 100644 index 0000000000..0b2ef59f56 --- /dev/null +++ b/app/src/main/res/layout/activity_full_screen_video.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_questions_edit.xml b/app/src/main/res/layout/activity_questions_edit.xml index c98305e049..2385a4d3c3 100644 --- a/app/src/main/res/layout/activity_questions_edit.xml +++ b/app/src/main/res/layout/activity_questions_edit.xml @@ -65,8 +65,45 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingLeft="20dp" - android:paddingRight="20dp"> + android:paddingLeft="16dp" + android:paddingRight="16dp"> + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_height="match_parent" /> + + + + + - + android:orientation="horizontal" + android:layout_marginBottom="4dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/editorTextNumTv" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constraintStart_toStartOf="parent"> - + + + + + + + + - - - + android:layout_alignParentBottom="true" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_video_publish.xml b/app/src/main/res/layout/activity_video_publish.xml new file mode 100644 index 0000000000..d0f1fc78f3 --- /dev/null +++ b/app/src/main/res/layout/activity_video_publish.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_video_tablayout_viewpager.xml b/app/src/main/res/layout/activity_video_tablayout_viewpager.xml index 4e1ebc21e1..c6497e830c 100644 --- a/app/src/main/res/layout/activity_video_tablayout_viewpager.xml +++ b/app/src/main/res/layout/activity_video_tablayout_viewpager.xml @@ -1,9 +1,9 @@ - + - + - - - - + + - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/community_answer_item.xml b/app/src/main/res/layout/community_answer_item.xml index 7456986130..5eb69753f6 100644 --- a/app/src/main/res/layout/community_answer_item.xml +++ b/app/src/main/res/layout/community_answer_item.xml @@ -21,7 +21,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" - android:paddingLeft="12dp" + android:paddingLeft="11dp" android:paddingRight="20dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" @@ -31,6 +31,7 @@ android:id="@+id/top_line" android:layout_width="match_parent" android:layout_height="@dimen/cutting_line" + android:layout_marginLeft="9dp" android:background="@color/cutting_line" android:visibility="gone" app:layout_constraintTop_toTopOf="parent" @@ -50,26 +51,27 @@ android:id="@+id/user_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="12dp" - app:avatar_width="32dp" + android:layout_marginTop="11dp" + app:avatar_width="36dp" app:badge_width="12dp" app:border_color="@color/black_alpha_10" app:border_width="1px" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@+id/top_line" - tools:layout_height="48dp" - tools:layout_width="48dp" /> + tools:layout_height="54dp" + tools:layout_width="54dp" /> + + + + + + - + android:gravity="center_vertical" + android:orientation="horizontal" + android:background="@drawable/bg_shape_f5_radius_999"> + + + + + + + - + app:layout_constraintTop_toTopOf="parent"> - + + + + + + - + + + + + + + + - - - + app:layout_constraintStart_toEndOf="@+id/community_edit_question_container" + app:layout_constraintTop_toTopOf="parent"> + + + + + + - - - + app:layout_constraintRight_toRightOf="parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/community_my_answer_item.xml b/app/src/main/res/layout/community_my_answer_item.xml index 4fb83e4682..91158dcd99 100644 --- a/app/src/main/res/layout/community_my_answer_item.xml +++ b/app/src/main/res/layout/community_my_answer_item.xml @@ -6,6 +6,8 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_choose_activity.xml b/app/src/main/res/layout/dialog_choose_activity.xml new file mode 100644 index 0000000000..8570eb61e6 --- /dev/null +++ b/app/src/main/res/layout/dialog_choose_activity.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_choose_forum.xml b/app/src/main/res/layout/dialog_choose_forum.xml index 30b893105c..29dbff9d52 100644 --- a/app/src/main/res/layout/dialog_choose_forum.xml +++ b/app/src/main/res/layout/dialog_choose_forum.xml @@ -2,14 +2,23 @@ + android:layout_height="match_parent" + android:background="@color/transparent" + android:focusable="true" + android:focusableInTouchMode="true"> + + + android:background="@drawable/game_detail_more_dialog_background"> - + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/line" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/editor_insert_container.xml b/app/src/main/res/layout/editor_insert_container.xml index 25c59d92cf..37a5798e44 100644 --- a/app/src/main/res/layout/editor_insert_container.xml +++ b/app/src/main/res/layout/editor_insert_container.xml @@ -1,315 +1,273 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_height="wrap_content" + android:orientation="vertical"> - - - - - - - - - - - - - - - - - - - - - - - - + android:background="@color/white" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + + + - - - - - - - - - - - - - - - - - - - - + android:layout_height="1dp" + android:layout_weight="1" /> - + android:layout_height="257dp" + android:background="@color/text_FAFAFA" + android:orientation="vertical" + android:visibility="gone"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/forum_activity_item.xml b/app/src/main/res/layout/forum_activity_item.xml new file mode 100644 index 0000000000..abd7e21f15 --- /dev/null +++ b/app/src/main/res/layout/forum_activity_item.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/forum_item.xml b/app/src/main/res/layout/forum_item.xml index c609e7b3ed..ee467695a9 100644 --- a/app/src/main/res/layout/forum_item.xml +++ b/app/src/main/res/layout/forum_item.xml @@ -6,6 +6,7 @@ + diff --git a/app/src/main/res/layout/forum_my_follow.xml b/app/src/main/res/layout/forum_my_follow.xml index 56a4efa749..5daec0454d 100644 --- a/app/src/main/res/layout/forum_my_follow.xml +++ b/app/src/main/res/layout/forum_my_follow.xml @@ -13,37 +13,59 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/reuse_listview_item_style" - android:gravity="center_vertical" - android:orientation="horizontal" - android:paddingLeft="20dp" - android:paddingTop="12dp" - android:paddingRight="20dp" - android:paddingBottom="12dp"> + android:orientation="vertical"> - + - + - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/forum_record_item.xml b/app/src/main/res/layout/forum_record_item.xml new file mode 100644 index 0000000000..5e5adef3bc --- /dev/null +++ b/app/src/main/res/layout/forum_record_item.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/forum_search_content_list.xml b/app/src/main/res/layout/forum_search_content_list.xml index eec4c08023..52b0653f9e 100644 --- a/app/src/main/res/layout/forum_search_content_list.xml +++ b/app/src/main/res/layout/forum_search_content_list.xml @@ -1,5 +1,6 @@ - @@ -11,121 +12,132 @@ - + android:layout_height="wrap_content"> - + - - - - - - - - - - - - - + android:background="@color/white" + android:orientation="vertical" + android:paddingLeft="20dp" + android:paddingTop="16dp" + android:paddingRight="20dp"> - + + + + + + + + + + + + + + + android:layout_marginTop="12dp" + android:layout_marginBottom="16dp"> - - + - - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/forum_welfare_item.xml b/app/src/main/res/layout/forum_welfare_item.xml new file mode 100644 index 0000000000..f27e95ecc9 --- /dev/null +++ b/app/src/main/res/layout/forum_welfare_item.xml @@ -0,0 +1,36 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_article_detail.xml b/app/src/main/res/layout/fragment_article_detail.xml index 1cb465af36..8bcd148075 100644 --- a/app/src/main/res/layout/fragment_article_detail.xml +++ b/app/src/main/res/layout/fragment_article_detail.xml @@ -4,8 +4,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:descendantFocusability="blocksDescendants" - android:background="@color/white"> + android:background="@color/white" + android:descendantFocusability="blocksDescendants"> - + + + + + + @@ -71,11 +84,13 @@ android:id="@+id/bottomShadowView" android:layout_width="match_parent" android:layout_height="6dp" - android:layout_above="@id/bottomContainer" + android:layout_above="@id/input_container" android:alpha="0.3" android:background="@drawable/shadow" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forum.xml b/app/src/main/res/layout/fragment_forum.xml new file mode 100644 index 0000000000..f7cfe4667c --- /dev/null +++ b/app/src/main/res/layout/fragment_forum.xml @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forum_activity.xml b/app/src/main/res/layout/fragment_forum_activity.xml new file mode 100644 index 0000000000..55550afaeb --- /dev/null +++ b/app/src/main/res/layout/fragment_forum_activity.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forum_detail.xml b/app/src/main/res/layout/fragment_forum_detail.xml index 3eaf7614cd..80fc3dc105 100644 --- a/app/src/main/res/layout/fragment_forum_detail.xml +++ b/app/src/main/res/layout/fragment_forum_detail.xml @@ -30,7 +30,6 @@ android:visibility="gone" tools:visibility="visible"> - + + + + @@ -116,93 +138,122 @@ android:id="@+id/forumNameTv" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginLeft="12dp" + android:layout_marginLeft="16dp" android:layout_marginRight="16dp" + android:singleLine="true" android:ellipsize="end" android:includeFontPadding="false" - android:maxLines="1" + android:textColor="@color/white" + android:textStyle="bold" + android:textSize="20sp" android:text="@{entity.name}" - android:textColor="@color/text_333333" - android:textSize="18sp" - app:layout_constraintBottom_toBottomOf="@+id/followTv" - app:layout_constraintEnd_toStartOf="@+id/followTv" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/followTv" - tools:text="剑与远征" /> - - + app:layout_constraintTop_toTopOf="parent" + tools:text="剑与远征剑与远征剑与远征剑与远征剑与远征剑与远征" /> - - - - - + android:src="@drawable/ic_forum_rule" /> + android:layout_marginLeft="5dp" + android:gravity="center" + android:textColor="@color/white_alpha_80" + android:textSize="10sp" + android:text="版规说明" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/forumNameTv" /> + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_forum_list.xml b/app/src/main/res/layout/fragment_forum_list.xml index f6014ea42c..7d81b5414b 100644 --- a/app/src/main/res/layout/fragment_forum_list.xml +++ b/app/src/main/res/layout/fragment_forum_list.xml @@ -1,5 +1,6 @@ @@ -9,12 +10,70 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forum_video_detail.xml b/app/src/main/res/layout/fragment_forum_video_detail.xml new file mode 100644 index 0000000000..e67022a123 --- /dev/null +++ b/app/src/main/res/layout/fragment_forum_video_detail.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_game_download_setting.xml b/app/src/main/res/layout/fragment_game_download_setting.xml new file mode 100644 index 0000000000..b16a119d7a --- /dev/null +++ b/app/src/main/res/layout/fragment_game_download_setting.xml @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index c947d1f7fd..ad0b6bf67f 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -576,63 +576,34 @@ android:src="@drawable/ic_change_bg" /> - + - - + android:text="@string/change_bg_tips" /> - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 25f448bc91..d07bd065be 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -110,6 +110,46 @@ + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_setting.xml b/app/src/main/res/layout/fragment_setting.xml index 4b826b5209..9d572954f1 100644 --- a/app/src/main/res/layout/fragment_setting.xml +++ b/app/src/main/res/layout/fragment_setting.xml @@ -13,7 +13,7 @@ + android:textColor="@color/text_333333" + android:textSize="14sp" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:paddingRight="20dp"> + android:text="@string/setting_video" + android:textColor="@color/text_333333" + android:textSize="14sp" /> - + android:layout_centerVertical="true" + android:src="@drawable/icon_more" /> + + + android:paddingRight="20dp"> + android:text="@string/setting_game_download" + android:textColor="@color/text_333333" + android:textSize="14sp" /> - + android:layout_centerVertical="true" + android:src="@drawable/icon_more" /> + + + + + + + + + + + @@ -319,8 +186,8 @@ android:layout_height="wrap_content" android:layout_centerVertical="true" android:text="@string/setting_install_method" - android:textColor="@color/title" - android:textSize="15sp" /> + android:textColor="@color/text_333333" + android:textSize="14sp" /> + + + + + + + + android:paddingTop="18dp" + android:paddingRight="16dp" + android:paddingBottom="18dp"> - + android:src="@drawable/selector_ic_setting_switch" /> + + - - - - - - - - - - - - - - - - - - + + - + @@ -509,7 +329,7 @@ android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:text="@string/setting_about" - android:textColor="@color/title" + android:textColor="@color/text_333333" android:textSize="15sp" /> @@ -539,30 +360,47 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" - android:text="@string/setting_logout" - android:textColor="@color/title" + android:text="@string/setting_clear_cache" + android:textColor="@color/text_333333" android:textSize="15sp" /> + android:textSize="12sp" /> + + + android:textColor="@color/text_333333" + android:textSize="15sp" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_user_comment_history.xml b/app/src/main/res/layout/fragment_user_comment_history.xml deleted file mode 100644 index c403a66a4c..0000000000 --- a/app/src/main/res/layout/fragment_user_comment_history.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_user_game.xml b/app/src/main/res/layout/fragment_user_game.xml new file mode 100644 index 0000000000..c718980a23 --- /dev/null +++ b/app/src/main/res/layout/fragment_user_game.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_user_publish.xml b/app/src/main/res/layout/fragment_user_publish.xml new file mode 100644 index 0000000000..4d26906ddf --- /dev/null +++ b/app/src/main/res/layout/fragment_user_publish.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_video_comment_list.xml b/app/src/main/res/layout/fragment_video_comment_list.xml new file mode 100644 index 0000000000..8cee1660b2 --- /dev/null +++ b/app/src/main/res/layout/fragment_video_comment_list.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_video_detail_skeleton.xml b/app/src/main/res/layout/fragment_video_detail_skeleton.xml new file mode 100644 index 0000000000..4d3792a9a2 --- /dev/null +++ b/app/src/main/res/layout/fragment_video_detail_skeleton.xml @@ -0,0 +1,19 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_video_publish.xml b/app/src/main/res/layout/fragment_video_publish.xml new file mode 100644 index 0000000000..007fd5dc43 --- /dev/null +++ b/app/src/main/res/layout/fragment_video_publish.xml @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_video_setting.xml b/app/src/main/res/layout/fragment_video_setting.xml new file mode 100644 index 0000000000..6ab5480065 --- /dev/null +++ b/app/src/main/res/layout/fragment_video_setting.xml @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/hot_forum_item.xml b/app/src/main/res/layout/hot_forum_item.xml new file mode 100644 index 0000000000..b351baaeb9 --- /dev/null +++ b/app/src/main/res/layout/hot_forum_item.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_article_detail_comment.xml b/app/src/main/res/layout/item_article_detail_comment.xml index 1354143c6d..e002b3243d 100644 --- a/app/src/main/res/layout/item_article_detail_comment.xml +++ b/app/src/main/res/layout/item_article_detail_comment.xml @@ -21,21 +21,21 @@ android:id="@+id/userIconIv" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:layout_marginTop="8dp" - app:avatar_width="32dp" + android:layout_marginLeft="11dp" + android:layout_marginTop="7dp" + app:avatar_width="36dp" app:badge_width="12dp" app:border_color="@color/transparent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:layout_height="48dp" - tools:layout_width="48dp" /> + tools:layout_height="54dp" + tools:layout_width="54dp" /> @@ -160,7 +160,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginRight="16dp" + android:layout_marginRight="20dp" android:ellipsize="end" android:includeFontPadding="false" android:lineSpacingExtra="6dp" diff --git a/app/src/main/res/layout/item_article_detail_comment_empty.xml b/app/src/main/res/layout/item_article_detail_comment_empty.xml index d5841ee065..1bc3a874a7 100644 --- a/app/src/main/res/layout/item_article_detail_comment_empty.xml +++ b/app/src/main/res/layout/item_article_detail_comment_empty.xml @@ -1,29 +1,23 @@ - + android:layout_height="288dp" + android:background="@color/white" + android:gravity="center" + android:orientation="vertical"> - + - - - - - - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_article_detail_content.xml b/app/src/main/res/layout/item_article_detail_content.xml index 9e2ab44ec4..bed283c41a 100644 --- a/app/src/main/res/layout/item_article_detail_content.xml +++ b/app/src/main/res/layout/item_article_detail_content.xml @@ -1,188 +1,214 @@ + - - - - - - - - + android:layout_height="wrap_content"> - + android:layout_marginLeft="11dp" + android:layout_marginTop="11dp" + app:avatar_width="36dp" + app:badge_width="12dp" + app:border_color="@color/black_alpha_10" + app:border_width="0.5dp" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:layout_height="54dp" + tools:layout_width="54dp" /> + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginLeft="2dp" + android:layout_marginTop="8dp" + android:layout_marginRight="16dp" + app:layout_constraintLeft_toRightOf="@id/userIconIv" + app:layout_constraintRight_toLeftOf="@+id/followBtn" + app:layout_constraintTop_toTopOf="@+id/userIconIv"> - - - + android:layout_height="0dp" + android:layout_marginLeft="3dp" + android:includeFontPadding="false" + android:maxLength="12" + android:textColor="@color/text_333333" + android:textSize="14sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/badgeContainer" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintWidth_default="wrap" + tools:text="我的昵称很长很长有十二字" /> + + + + - - - - - - - - - - - - - + android:gravity="center_vertical" + android:includeFontPadding="false" + android:textColor="@color/text_999999" + android:textSize="10sp" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="@id/badgeIv" + app:layout_constraintLeft_toRightOf="@id/badgeIv" + app:layout_constraintRight_toLeftOf="@+id/followBtn" + app:layout_constraintTop_toTopOf="@id/badgeIv" + tools:text="答题先锋答题先锋答题先锋" + tools:visibility="visible" /> + - - + android:textSize="11sp" + app:layout_constraintBottom_toBottomOf="@id/userIconIv" + app:layout_constraintLeft_toRightOf="@id/userIconIv" + tools:text="发布于1分钟前" /> - + - + - + + + + + + + + + + + + + + + - + diff --git a/app/src/main/res/layout/item_forum_video.xml b/app/src/main/res/layout/item_forum_video.xml new file mode 100644 index 0000000000..68f3f26efc --- /dev/null +++ b/app/src/main/res/layout/item_forum_video.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_user_comment_history.xml b/app/src/main/res/layout/item_user_comment_history.xml index 151c8bf5a1..b6f1d83b9f 100644 --- a/app/src/main/res/layout/item_user_comment_history.xml +++ b/app/src/main/res/layout/item_user_comment_history.xml @@ -26,14 +26,15 @@ android:id="@+id/divider" android:layout_width="match_parent" android:layout_height="1dp" - android:background="@color/F2F2F2" /> + android:background="@color/EEEEEE" /> + android:layout_marginTop="16dp" + android:orientation="vertical" + android:background="@drawable/bg_user_comment_game_info"> - - - - - - - - + app:layout_constraintTop_toTopOf="@id/score_icon" /> + + + + + + - - diff --git a/app/src/main/res/layout/item_video_desc_top.xml b/app/src/main/res/layout/item_video_desc_top.xml new file mode 100644 index 0000000000..61ae95ba0f --- /dev/null +++ b/app/src/main/res/layout/item_video_desc_top.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_video_selector.xml b/app/src/main/res/layout/item_video_selector.xml new file mode 100644 index 0000000000..34fa87a15e --- /dev/null +++ b/app/src/main/res/layout/item_video_selector.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_article_item_video.xml b/app/src/main/res/layout/layout_article_item_video.xml new file mode 100644 index 0000000000..32605603bb --- /dev/null +++ b/app/src/main/res/layout/layout_article_item_video.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_forum_activity_category_item.xml b/app/src/main/res/layout/layout_forum_activity_category_item.xml new file mode 100644 index 0000000000..f0bc95b6bf --- /dev/null +++ b/app/src/main/res/layout/layout_forum_activity_category_item.xml @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_forum_video_detail_videoview_portrait.xml b/app/src/main/res/layout/layout_forum_video_detail_videoview_portrait.xml new file mode 100644 index 0000000000..074a248230 --- /dev/null +++ b/app/src/main/res/layout/layout_forum_video_detail_videoview_portrait.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_full_screen_detail_video_portrait.xml b/app/src/main/res/layout/layout_full_screen_detail_video_portrait.xml new file mode 100644 index 0000000000..cca446da1f --- /dev/null +++ b/app/src/main/res/layout/layout_full_screen_detail_video_portrait.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_menu_article_detail.xml b/app/src/main/res/layout/layout_menu_article_detail.xml new file mode 100644 index 0000000000..a9a5e79964 --- /dev/null +++ b/app/src/main/res/layout/layout_menu_article_detail.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/app/src/main/res/layout/layout_preview_video.xml b/app/src/main/res/layout/layout_preview_video.xml new file mode 100644 index 0000000000..453035c1e9 --- /dev/null +++ b/app/src/main/res/layout/layout_preview_video.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/local_video_item.xml b/app/src/main/res/layout/local_video_item.xml index e5b3c1bd23..6ec7896003 100644 --- a/app/src/main/res/layout/local_video_item.xml +++ b/app/src/main/res/layout/local_video_item.xml @@ -8,20 +8,47 @@ + app:roundingBorderColor="@color/black_alpha_10" + app:roundingBorderWidth="0.5dp" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/personal_home_item.xml b/app/src/main/res/layout/personal_home_item.xml deleted file mode 100644 index 373f28bd9f..0000000000 --- a/app/src/main/res/layout/personal_home_item.xml +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/piece_article_input_container.xml b/app/src/main/res/layout/piece_article_input_container.xml index c4f738cdc6..28027de83e 100644 --- a/app/src/main/res/layout/piece_article_input_container.xml +++ b/app/src/main/res/layout/piece_article_input_container.xml @@ -8,6 +8,7 @@ android:layout_alignParentBottom="true" android:background="@color/white" android:clickable="true" + android:focusable="true" tools:showIn="@layout/fragment_article_detail"> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/piece_community_vote_and_comment.xml b/app/src/main/res/layout/piece_community_vote_and_comment.xml index 40367d5bc8..98357099ae 100644 --- a/app/src/main/res/layout/piece_community_vote_and_comment.xml +++ b/app/src/main/res/layout/piece_community_vote_and_comment.xml @@ -22,7 +22,7 @@ android:layout_height="match_parent" android:layout_gravity="center" android:drawableLeft="@drawable/community_comment_count" - android:drawablePadding="8dp" + android:drawablePadding="4dp" android:gravity="center" android:paddingTop="20dp" android:paddingBottom="20dp" diff --git a/app/src/main/res/layout/piece_forum_top_video_replay.xml b/app/src/main/res/layout/piece_forum_top_video_replay.xml new file mode 100644 index 0000000000..a65d2f0e47 --- /dev/null +++ b/app/src/main/res/layout/piece_forum_top_video_replay.xml @@ -0,0 +1,44 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/piece_setting_divider.xml b/app/src/main/res/layout/piece_setting_divider.xml new file mode 100644 index 0000000000..2e5c1773c2 --- /dev/null +++ b/app/src/main/res/layout/piece_setting_divider.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/tab_item_forum_video_detail.xml b/app/src/main/res/layout/tab_item_forum_video_detail.xml new file mode 100644 index 0000000000..6d6648bb11 --- /dev/null +++ b/app/src/main/res/layout/tab_item_forum_video_detail.xml @@ -0,0 +1,29 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/user_history_item.xml b/app/src/main/res/layout/user_history_item.xml new file mode 100644 index 0000000000..225931fa36 --- /dev/null +++ b/app/src/main/res/layout/user_history_item.xml @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/video_label_item.xml b/app/src/main/res/layout/video_label_item.xml index f114ce7351..04751697c5 100644 --- a/app/src/main/res/layout/video_label_item.xml +++ b/app/src/main/res/layout/video_label_item.xml @@ -13,40 +13,48 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" - android:padding="20dp"> + android:paddingLeft="16dp" + android:paddingTop="20dp" + android:paddingRight="16dp" + android:paddingBottom="20dp"> + tools:text="发现好游戏计划" /> diff --git a/app/src/main/res/menu/menu_article_detail.xml b/app/src/main/res/menu/menu_article_detail.xml new file mode 100644 index 0000000000..0d62f21478 --- /dev/null +++ b/app/src/main/res/menu/menu_article_detail.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_forum_video_detail.xml b/app/src/main/res/menu/menu_forum_video_detail.xml new file mode 100644 index 0000000000..282fb29378 --- /dev/null +++ b/app/src/main/res/menu/menu_forum_video_detail.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 30087b4f64..cbded751b4 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -157,4 +157,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index eb517a59a4..14e853e224 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -93,6 +93,7 @@ #0D000000 #1A000000 #4D000000 + #99000000 #66000000 #80000000 @@ -124,6 +125,7 @@ #d6d5ff #f8f8f8 #e5e5e5 + #e6e6e6 #ECF6FF #EDEDED #EEEEEE @@ -151,6 +153,12 @@ #91F0FD #F6FBFF #50DCDC + #81B5FF + #20FFB5 + #00DDDD + #74FFFF + #60D5FF + #EDF5FC #b2b2b2 #9a9a9a #3a3a3a @@ -252,6 +260,9 @@ #3682C2 #F67722 #4B4B4B + #FF925C + #3087D9 + #C0C6CC #99666666 #6621282E diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f82f98c06..e421895493 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -130,6 +130,8 @@ 为了避免消耗流量,请关闭你手机的移动网络 从手机相册中选择 账号与安全 + 视频播放 + 游戏下载 一键修复 下载完成自动安装游戏 安装完成自动关注游戏 @@ -154,11 +156,16 @@ 图片保存目录 清除缓存 0.0M - 关于 + 关于我们 用户协议 隐私政策 安装包清理 游戏投稿 + 任何网络下都自动播放 + 仅WIFI条件下自动播放 + 关闭自动播放 + 默认静音播放视频 + 开启后以上位置的视频均默认静音播放 1 创建热点 正在创建热点... @@ -515,7 +522,7 @@ 请输入正确的链接 登录成功 问答 - 论坛 + 社区 推荐 问题 @@ -764,6 +771,7 @@ 取消 确定 + 点击更换背景 提交反馈 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 417428d210..2ccce10257 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -69,6 +69,13 @@ + + + + \ No newline at end of file diff --git a/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java b/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java index 92dfa27657..c966516e4a 100644 --- a/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java +++ b/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java @@ -44,8 +44,8 @@ public class AlbumsSpinner { mListPopupWindow.setModal(true); float density = context.getResources().getDisplayMetrics().density; mListPopupWindow.setContentWidth((int) (216 * density)); - mListPopupWindow.setHorizontalOffset((int) (16 * density)); - mListPopupWindow.setVerticalOffset((int) (-48 * density)); +// mListPopupWindow.setHorizontalOffset((int) (16 * density)); +// mListPopupWindow.setVerticalOffset((int) (-48 * density)); mListPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { diff --git a/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java b/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java index 7407325000..053a71b3fa 100644 --- a/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java +++ b/libraries/Matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java @@ -37,11 +37,11 @@ import com.zhihu.matisse.R; public class CheckView extends View { public static final int UNCHECKED = Integer.MIN_VALUE; - private static final float STROKE_WIDTH = 3.0f; // dp + private static final float STROKE_WIDTH = 1.0f; // dp private static final float SHADOW_WIDTH = 6.0f; // dp - private static final int SIZE = 48; // dp - private static final float STROKE_RADIUS = 11.5f; // dp - private static final float BG_RADIUS = 11.0f; // dp + private static final int SIZE = 32; // dp + private static final float STROKE_RADIUS = 8f; // dp + private static final float BG_RADIUS = 7.5f; // dp private static final int CONTENT_SIZE = 16; // dp private boolean mCountable; private boolean mChecked; @@ -132,9 +132,9 @@ public class CheckView extends View { super.onDraw(canvas); // draw outer and inner shadow - initShadowPaint(); - canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2, - (STROKE_RADIUS + STROKE_WIDTH / 2 + SHADOW_WIDTH) * mDensity, mShadowPaint); +// initShadowPaint(); +// canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2, +// (STROKE_RADIUS + STROKE_WIDTH / 2 + SHADOW_WIDTH) * mDensity, mShadowPaint); // draw white stroke canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2, @@ -212,7 +212,7 @@ public class CheckView extends View { mTextPaint.setAntiAlias(true); mTextPaint.setColor(Color.WHITE); mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); - mTextPaint.setTextSize(12.0f * mDensity); + mTextPaint.setTextSize(10.0f * mDensity); } } diff --git a/libraries/Matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java b/libraries/Matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java index 5d16059838..f23eeea14b 100644 --- a/libraries/Matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java +++ b/libraries/Matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java @@ -152,7 +152,7 @@ public class MatisseActivity extends AppCompatActivity implements mAlbumsSpinner = new AlbumsSpinner(this); mAlbumsSpinner.setOnItemSelectedListener(this); mAlbumsSpinner.setSelectedTextView((TextView) findViewById(R.id.selected_album)); - mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.toolbar)); + mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.selected_album)); mAlbumsSpinner.setAdapter(mAlbumsAdapter); mAlbumCollection.onCreate(this, this); mAlbumCollection.onRestoreInstanceState(savedInstanceState); diff --git a/libraries/Matisse/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png b/libraries/Matisse/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png index da239e4f4e..3ed05490c1 100644 Binary files a/libraries/Matisse/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png and b/libraries/Matisse/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png differ diff --git a/libraries/Matisse/src/main/res/layout/activity_matisse.xml b/libraries/Matisse/src/main/res/layout/activity_matisse.xml index 2d2528cb34..9f8363e615 100644 --- a/libraries/Matisse/src/main/res/layout/activity_matisse.xml +++ b/libraries/Matisse/src/main/res/layout/activity_matisse.xml @@ -13,14 +13,40 @@ See the License for the specific language governing permissions and limitations under the License. --> - + - + + + + + + + + android:id="@+id/bottom_toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:background="?attr/bottomToolbar.bg" + android:elevation="4dp" + tools:targetApi="lollipop"> + android:id="@+id/button_preview" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="start" + android:foreground="?selectableItemBackground" + android:padding="16dp" + android:text="@string/button_preview" + android:textColor="?attr/bottomToolbar.preview.textColor" + android:textSize="16sp" /> + android:id="@+id/originalLayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:foreground="?selectableItemBackground" + android:orientation="horizontal" + android:padding="16dp" + android:visibility="visible" + tools:showIn="@layout/activity_matisse"> + android:id="@+id/original" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_gravity="center_vertical" + android:src="@drawable/ic_preview_radio_off" /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:enabled="true" + android:paddingStart="4dp" + android:paddingLeft="4dp" + android:text="@string/button_original" + android:textColor="?attr/bottomToolbar.preview.textColor" + android:textSize="14sp" /> - + - + android:id="@+id/button_apply" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:foreground="?selectableItemBackground" + android:padding="16dp" + android:textColor="?attr/bottomToolbar.apply.textColor" + android:textSize="16sp" /> + + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_above="@id/bottom_toolbar" + android:layout_below="@id/toolbar_container" + android:visibility="gone" /> + android:id="@+id/empty_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_above="@id/bottom_toolbar" + android:layout_below="@id/toolbar_container" + android:visibility="gone"> + android:id="@+id/empty_view_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:drawableTop="?attr/album.emptyView" + android:drawablePadding="8dp" + android:gravity="center" + android:text="@string/empty_text" + android:textColor="?attr/album.emptyView.textColor" + android:textSize="16sp" /> - - \ No newline at end of file + + \ No newline at end of file