feat:媒体文件上传控件优化 (二、三)—客户端 https://jira.shanqu.cc/browse/GHZSCY-6645

This commit is contained in:
张晨
2024-11-06 15:09:47 +08:00
parent 56151bc38d
commit c29efbefd7
51 changed files with 1669 additions and 690 deletions

View File

@ -21,26 +21,31 @@ import com.gh.common.view.RichEditor
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.qa.editor.*
import com.gh.gamecenter.entity.GamesCollectionEntity
import com.gh.gamecenter.entity.MyVideoEntity
import com.gh.gamecenter.entity.VideoEntity
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.qa.editor.*
import com.gh.gamecenter.qa.entity.EditorInsertEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.poster.video.VideoPosterFragment
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.lightgame.view.CheckableImageView
import com.therouter.TheRouter
import io.reactivex.disposables.Disposable
import org.json.JSONArray
import org.json.JSONObject
@ -229,6 +234,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
@SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
VideoPosterFragment.createVideoCoverFile(this)
findView()
onRichClick()
mViewModel = provideViewModel()
@ -739,9 +745,9 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
mViewModel.id = id
mViewModel.videoId = videoId
val videoEntity = VideoEntity(url = url)
val intent =
PosterEditActivity.getIntentByVideo(this@BaseRichEditorActivity, videoEntity)
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
TheRouter.build(RouteConsts.activity.videoCoverEditActivity)
.withParcelable(EntranceConsts.KEY_VIDEO_ENTITY, videoEntity)
.navigation(this@BaseRichEditorActivity, REQUEST_CODE_IMAGE_CROP)
}
@JavascriptInterface

View File

@ -0,0 +1,58 @@
package com.gh.common.provider
import android.content.Context
import android.content.Intent
import android.net.Uri
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.core.provider.ICropImageProvider
import com.gh.gamecenter.personalhome.background.BackgroundClipActivity
import com.halo.assistant.fragment.user.UserPortraitCropImageActivity
import com.lightgame.utils.Utils
import com.zhihu.matisse.internal.utils.PathUtils
@com.therouter.inject.ServiceProvider
class CropImageProviderImpl : ICropImageProvider {
override fun getCropImageIntent(data: List<Uri>, imageType: Int, entrance: String, context: Context): Intent? {
if (data.isEmpty()) {
return null
}
val picturePath = PathUtils.getPath(context, data[0])
Utils.log("picturePath = $picturePath")
return when (imageType) {
IMAGE_TYPE_AVATAR -> {// 上传头像
UserPortraitCropImageActivity.getIntent(
context,
picturePath,
"我的光环(选择头像)"
)
}
IMAGE_TYPE_GAME_COLLECTION_COVER -> {// 游戏单封面
CropImageActivity.getIntent(
context,
picturePath,
142 / 328F,
false,
R.layout.layout_game_collection_crop_image_assist,
entrance
)
}
IMAGE_TYPE_PERSONAL_BACKGROUND -> { // 用户主页背景
BackgroundClipActivity.getIntent(context, picturePath, entrance)
}
else ->
null
}
}
companion object {
const val IMAGE_TYPE_AVATAR = 1
const val IMAGE_TYPE_GAME_COLLECTION_COVER = 2
const val IMAGE_TYPE_PERSONAL_BACKGROUND = 3
}
}

View File

@ -3,26 +3,25 @@ package com.gh.gamecenter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import com.gh.gamecenter.common.base.activity.ToolBarActivity;
import com.gh.gamecenter.common.utils.BitmapUtils;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.common.constant.EntranceConsts;
import com.gh.gamecenter.common.utils.BitmapUtils;
import com.gh.gamecenter.common.view.CropImageCustom;
import com.gh.gamecenter.common.view.cropbox.CropBoxStyle;
import com.gh.gamecenter.core.utils.DisplayUtils;
import java.io.File;
import java.lang.ref.SoftReference;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
/**
* 裁剪图片
*/
@ -32,6 +31,8 @@ public class CropImageActivity extends ToolBarActivity {
public static final String RESULT_CLIP_PATH = "result_clip_path";
public static final String RESULT_ORIGINAL_PATH = "result_original_path";
private SoftReference<Bitmap> reference;
protected boolean mBlackTheme = false;
@ -51,7 +52,7 @@ public class CropImageActivity extends ToolBarActivity {
Intent intent = new Intent(context, CropImageActivity.class);
intent.putExtra(EntranceConsts.KEY_PATH, picturePath);
intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance);
intent.putExtra(EntranceConsts.KEY_IMAGE_CROP_RATIO, cropRatio);
intent.putExtra(EntranceConsts.KEY_IMAGE_CROP_STYLE, new CropBoxStyle.Rectangle(cropRatio));
intent.putExtra(EntranceConsts.KEY_BLACK_THEME, isBlackTheme);
intent.putExtra(EntranceConsts.KEY_ASSIST_RES, assistRes);
return intent;
@ -68,20 +69,14 @@ public class CropImageActivity extends ToolBarActivity {
super.onCreate(savedInstanceState);
mCropImageCustom = findViewById(R.id.cropimage_custom);
View statusBarView = findViewById(R.id.status_bar);
TextView tvCancel = findViewById(R.id.tv_cancel);
TextView tvSubmit = findViewById(R.id.tv_submit);
mTitleTv.setTextColor(mBlackTheme ? Color.WHITE : Color.BLACK);
mToolbar.setBackgroundColor(getResources().getColor(mBlackTheme ? com.gh.gamecenter.common.R.color.text_28282E : com.gh.gamecenter.common.R.color.white));
statusBarView.setBackgroundColor(getResources().getColor(mBlackTheme ? com.gh.gamecenter.common.R.color.text_28282E : com.gh.gamecenter.common.R.color.white));
setNavigationTitle(getString(R.string.title_crop_image));
setToolbarMenu(R.menu.menu_positive);
MenuItem menuItem = getMenuItem(R.id.layout_menu_positive);
TextView menuButton = menuItem.getActionView().findViewById(R.id.menu_answer_post);
menuButton.setTextColor(getResources().getColor(com.gh.gamecenter.common.R.color.text_theme));
float ratio = getIntent().getFloatExtra(EntranceConsts.KEY_IMAGE_CROP_RATIO, 1F);
mCropImageCustom.setCropRatio(ratio);
CropBoxStyle boxStyle = getIntent().getParcelableExtra(EntranceConsts.KEY_IMAGE_CROP_STYLE);
if (boxStyle == null) {
boxStyle = new CropBoxStyle.Rectangle(1F);
}
mCropImageCustom.setCropBoxStyle(boxStyle);
int assistRes = getIntent().getIntExtra(EntranceConsts.KEY_ASSIST_RES, -1);
if (assistRes > 0) {
@ -89,8 +84,21 @@ public class CropImageActivity extends ToolBarActivity {
addAssistView(view);
}
DisplayUtils.setLightStatusBar(this, !mBlackTheme);
DisplayUtils.setStatusBarColor(this, com.gh.gamecenter.common.R.color.transparent, !mBlackTheme);
DisplayUtils.setLightStatusBar(this, false);
DisplayUtils.setStatusBarColor(this, com.gh.gamecenter.common.R.color.transparent, false);
tvCancel.setOnClickListener(v -> finish());
tvSubmit.setOnClickListener(v -> saveImage());
}
protected void saveImage() {
Intent data = new Intent();
String clipPath = getCacheDir().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg";
mCropImageCustom.savePicture(clipPath);
data.putExtra(RESULT_CLIP_PATH, clipPath);
setResult(RESULT_OK, data);
finish();
}
@Override
@ -98,20 +106,6 @@ public class CropImageActivity extends ToolBarActivity {
return mBlackTheme ? com.gh.gamecenter.common.R.drawable.ic_toolbar_back_white : com.gh.gamecenter.common.R.drawable.ic_bar_back;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.layout_menu_positive) {
Intent data = new Intent();
String clipPath = getCacheDir().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg";
mCropImageCustom.savePicture(clipPath);
data.putExtra(RESULT_CLIP_PATH, clipPath);
setResult(RESULT_OK, data);
finish();
}
return super.onMenuItemClick(item);
}
public void addAssistView(View view) {
mCropImageCustom.addAssistView(view);
}

View File

@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import com.gh.common.provider.CropImageProviderImpl.Companion.IMAGE_TYPE_GAME_COLLECTION_COVER
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.common.util.NewLogUtils
@ -45,7 +46,8 @@ class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
requireContext(),
ChooseType.IMAGE,
1,
"创建游戏单"
"创建游戏单",
IMAGE_TYPE_GAME_COLLECTION_COVER
), REQUEST_CODE_IMAGE
)
}
@ -101,7 +103,8 @@ class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
com.gh.gamecenter.common.R.drawable.bg_choose_option_selector.toDrawable(requireContext())
defaultUploadContainer.background =
com.gh.gamecenter.common.R.drawable.bg_choose_option_selector.toDrawable(requireContext())
cancelBtn.background = com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999.toDrawable(requireContext())
cancelBtn.background =
com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999.toDrawable(requireContext())
recommentTv.background = R.drawable.bg_game_collection_cover_tag.toDrawable(requireContext())
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(requireContext()))
localUploadTitleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))

View File

@ -313,14 +313,14 @@ class GameCollectionEditActivity : ToolBarActivity(), ChooseGamesAdapter.ItemDra
val games = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf()
if (mIsCreateGameCollection) {
SensorsBridge.trackEvent("GameCollectCreateSuccess", json {
"game_collect_status" to if(mBinding.selfOnlyCb.isChecked) "仅自己可见" else "公开"
"game_collect_status" to if (mBinding.selfOnlyCb.isChecked) "仅自己可见" else "公开"
"game_num" to games.size
"game_collect_title" to mBinding.gameCollectionTitleEt.text.toString().trim()
"game_collect_id" to it.data as String
})
} else if (mViewModel.gameCollectionPatch != null) {
SensorsBridge.trackEvent("GameCollectEditSuccess", json {
"game_collect_status" to if(mBinding.selfOnlyCb.isChecked) "仅自己可见" else "公开"
"game_collect_status" to if (mBinding.selfOnlyCb.isChecked) "仅自己可见" else "公开"
"game_num" to games.size
"game_collect_title" to mBinding.gameCollectionTitleEt.text.toString().trim()
"game_collect_id" to mViewModel.gameCollectionPatch?.id
@ -355,7 +355,7 @@ class GameCollectionEditActivity : ToolBarActivity(), ChooseGamesAdapter.ItemDra
CheckLoginUtils.checkLogin(this, mEntrance) {}
return@observe
}
ErrorHelper.handleError(this, errorMsg, false, "发布游戏单", "社区实名") {
ErrorHelper.handleError(this, errorMsg, false, "发布游戏单", "社区实名") {
if (::mMenuPost.isInitialized) {
onMenuItemClick(mMenuPost)
}
@ -433,18 +433,6 @@ class GameCollectionEditActivity : ToolBarActivity(), ChooseGamesAdapter.ItemDra
super.onActivityResult(requestCode, resultCode, data)
if (data == null || resultCode != Activity.RESULT_OK) return
when (requestCode) {
REQUEST_CODE_IMAGE_CROP -> {
val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: ""
mViewModel.imageUrl = ""
mViewModel.imagePath = imagePath
if (imagePath.isEmpty()) {
mBinding.uploadPictureTv.text = "点击上传图片"
} else {
mBinding.uploadPictureTv.text = "图片上传中..."
mViewModel.uploadPoster()
}
initPosterUI()
}
REQUEST_CHOOSE_TAG -> {
val tags = data.getParcelableArrayListExtra<TagInfoEntity>(GameCollectionTagSelectFragment.SELECTED_TAG)
?: arrayListOf()
@ -457,20 +445,16 @@ class GameCollectionEditActivity : ToolBarActivity(), ChooseGamesAdapter.ItemDra
fun onActivityDialogResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && data != null) {
if (requestCode == REQUEST_CODE_IMAGE) {
val selectedPaths = Matisse.obtainResult(data)
if (!selectedPaths.isNullOrEmpty()) {
val path = PathUtils.getPath(this, selectedPaths[0])
val intent =
CropImageActivity.getIntent(
this,
path,
142 / 328F,
false,
R.layout.layout_game_collection_crop_image_assist,
mEntrance
)
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: ""
mViewModel.imageUrl = ""
mViewModel.imagePath = imagePath
if (imagePath.isEmpty()) {
mBinding.uploadPictureTv.text = "点击上传图片"
} else {
mBinding.uploadPictureTv.text = "图片上传中..."
mViewModel.uploadPoster()
}
initPosterUI()
} else if (requestCode == ChooseGameCollectionDefaultCoverDialog.REQUEST_CODE_DEFAULT_IMAGE) {
val entity = data.getParcelableExtra<GameCollectionCoverEntity>(EntranceConsts.KEY_DATA)
if (entity != null) {
@ -731,9 +715,17 @@ class GameCollectionEditActivity : ToolBarActivity(), ChooseGamesAdapter.ItemDra
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
} else {
DialogHelper.showDialog(this, "温馨提示", "游戏单会在1-2个工作日内审核完成您可以在“我的光环-我的游戏单”查看进度", "继续提交", "取消", {
mViewModel.uploadContent(this, requestMap)
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
DialogHelper.showDialog(
this,
"温馨提示",
"游戏单会在1-2个工作日内审核完成您可以在“我的光环-我的游戏单”查看进度",
"继续提交",
"取消",
{
mViewModel.uploadContent(this, requestMap)
},
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
}
}
@ -804,7 +796,8 @@ class GameCollectionEditActivity : ToolBarActivity(), ChooseGamesAdapter.ItemDra
activityDivider.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_divider.toColor(this@GameCollectionEditActivity))
titleDivider.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_divider.toColor(this@GameCollectionEditActivity))
introduceDivider.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_divider.toColor(this@GameCollectionEditActivity))
placeholderView.background = com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_8.toDrawable(this@GameCollectionEditActivity)
placeholderView.background =
com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_8.toDrawable(this@GameCollectionEditActivity)
selfOnlyCb.background =
R.drawable.border_round_stroke_0dot5_eee_999.toDrawable(this@GameCollectionEditActivity)
uploadPictureTv.setTextColor(com.gh.gamecenter.common.R.color.text_instance.toColor(this@GameCollectionEditActivity))
@ -912,7 +905,6 @@ class GameCollectionEditActivity : ToolBarActivity(), ChooseGamesAdapter.ItemDra
companion object {
const val REQUEST_CODE_IMAGE = 100
const val REQUEST_CODE_IMAGE_CROP = 101
const val REQUEST_CHOOSE_GAMES = 102
const val REQUEST_CHOOSE_TAG = 103
const val REQUEST_CHOOSE_ACTIVITY = 105

View File

@ -6,14 +6,16 @@ import android.content.Intent
import android.graphics.Bitmap
import android.os.Bundle
import android.widget.ImageView
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.utils.BitmapUtils
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.common.provider.CropImageProviderImpl.Companion.IMAGE_TYPE_PERSONAL_BACKGROUND
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.BitmapUtils
import com.gh.gamecenter.common.view.cropbox.CropBoxStyle
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.ActivityBackgroundClipBinding
import java.io.File
import java.lang.ref.SoftReference
@ -33,7 +35,6 @@ class BackgroundClipActivity : BaseActivity() {
super.onCreate(savedInstanceState)
DisplayUtils.transparentStatusBar(this)
mBinding = ActivityBackgroundClipBinding.bind(mContentView)
mBinding.cropImageIv.setCropRatio(392 / 360F)
mBinding.cancelTv.setOnClickListener {
finish()
@ -84,6 +85,8 @@ class BackgroundClipActivity : BaseActivity() {
val intent = Intent(context, BackgroundClipActivity::class.java)
intent.putExtra(EntranceConsts.KEY_PATH, picturePath)
intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance)
intent.putExtra(EntranceConsts.KEY_IMAGE_CROP_STYLE, CropBoxStyle.Rectangle(356F / 328))
intent.putExtra(EntranceConsts.KEY_IMAGE_TYPE, IMAGE_TYPE_PERSONAL_BACKGROUND)
return intent
}
}

View File

@ -8,6 +8,7 @@ import android.view.View
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.provider.CropImageProviderImpl.Companion.IMAGE_TYPE_PERSONAL_BACKGROUND
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
@ -78,7 +79,8 @@ class PersonalityBackgroundFragment : ToolbarFragment() {
requireContext(),
ChooseType.IMAGE,
1,
"个性背景"
"个性背景",
IMAGE_TYPE_PERSONAL_BACKGROUND
), MEDIA_STORE_REQUEST
)
}
@ -89,19 +91,15 @@ class PersonalityBackgroundFragment : ToolbarFragment() {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == MEDIA_STORE_REQUEST && resultCode == Activity.RESULT_OK) {
val selectedPaths = Matisse.obtainPathResult(data)
if (!selectedPaths.isNullOrEmpty()) {
val intent = BackgroundClipActivity.getIntent(requireContext(), selectedPaths[0], mEntrance)
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
}
} else if (requestCode == REQUEST_CODE_IMAGE_CROP && resultCode == Activity.RESULT_OK) {
if (data != null) {
val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH)
val intent = BackgroundPreviewActivity.getIntent(
requireContext(), imagePath
?: "", null
)
requireActivity().startActivityForResult(intent, CHANGE_BACKGROUND_SUCCESS)
if (imagePath != null) {
val intent = BackgroundPreviewActivity.getIntent(
requireContext(), imagePath
?: "", null
)
requireActivity().startActivityForResult(intent, CHANGE_BACKGROUND_SUCCESS)
}
}
} else if (requestCode == CHANGE_BACKGROUND_SUCCESS && resultCode == Activity.RESULT_OK) {
requireActivity().finish()
@ -119,7 +117,6 @@ class PersonalityBackgroundFragment : ToolbarFragment() {
}
companion object {
const val REQUEST_CODE_IMAGE_CROP = 100
const val MEDIA_STORE_REQUEST = 101
const val CHANGE_BACKGROUND_SUCCESS = 102
}

View File

@ -40,6 +40,7 @@ import com.gh.gamecenter.qa.editor.GameActivity
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.qa.questions.edit.TagsSelectFragment
import com.gh.gamecenter.qa.video.publish.VideoPublishFragment
import com.gh.gamecenter.video.poster.video.VideoPosterFragment
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import org.greenrobot.eventbus.EventBus
@ -123,6 +124,7 @@ class ArticleEditActivity : BaseRichEditorActivity<ArticleEditViewModel>(), Keyb
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
VideoPosterFragment.clearVideoCoverCache(this)
updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface)
mBinding = ActivityCommunityArticleEditBinding.bind(mContentView)
setToolbarMenu(R.menu.menu_answer_post)
@ -651,8 +653,14 @@ class ArticleEditActivity : BaseRichEditorActivity<ArticleEditViewModel>(), Keyb
private fun setForumUI() {
mBinding.forumIconView.visibility = View.VISIBLE
mBinding.forumContainer.background = ContextCompat.getDrawable(this, com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999)
mBinding.articleGameName.setTextColor(ContextCompat.getColor(this, com.gh.gamecenter.common.R.color.text_secondary))
mBinding.forumContainer.background =
ContextCompat.getDrawable(this, com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999)
mBinding.articleGameName.setTextColor(
ContextCompat.getColor(
this,
com.gh.gamecenter.common.R.color.text_secondary
)
)
}
private fun setSectionUI(isEmpty: Boolean) {

View File

@ -50,6 +50,7 @@ import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
import com.gh.gamecenter.qa.questions.draft.QuestionDraftActivity
import com.gh.gamecenter.qa.questions.edit.tip.QuestionTitleTipAdapter
import com.gh.gamecenter.qa.video.publish.VideoPublishFragment
import com.gh.gamecenter.video.poster.video.VideoPosterFragment
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import org.greenrobot.eventbus.EventBus
@ -147,6 +148,7 @@ class QuestionEditActivity : BaseRichEditorActivity<QuestionEditViewModel>(),
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
VideoPosterFragment.clearVideoCoverCache(this)
updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface)
setToolbarMenu(R.menu.menu_question_post)
mToolbar.navigationIcon = null
@ -280,12 +282,14 @@ class QuestionEditActivity : BaseRichEditorActivity<QuestionEditViewModel>(),
detailEntity != null -> { // 问题编辑
setPatchContent(detailEntity)
}
draftEntity != null -> { //草稿编辑
mViewModel.questionDraftEntity = draftEntity
setQuestionDraft(draftEntity)
mViewModel.getQuestionDraftContent(draftEntity.id)
mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong())
}
else -> { // 新增问题
var searchKey = intent.getStringExtra(EntranceConsts.KEY_QUESTIONS_SEARCH_KEY)
if (!searchKey.isNullOrEmpty() && searchKey.length > QuestionEditViewModel.QUESTION_TITLE_MAX_LENGTH)
@ -468,6 +472,7 @@ class QuestionEditActivity : BaseRichEditorActivity<QuestionEditViewModel>(),
finish()
}
}
SaveDraftType.AUTO -> {
if (pair.second) {
if (mPostDraftsCount >= SAVE_DRAFTS_TOAST_COUNT) {
@ -478,6 +483,7 @@ class QuestionEditActivity : BaseRichEditorActivity<QuestionEditViewModel>(),
}
}
}
SaveDraftType.SKIP -> {
if (pair.second) {
Utils.toast(this, "问题已保存到草稿箱")
@ -690,7 +696,12 @@ class QuestionEditActivity : BaseRichEditorActivity<QuestionEditViewModel>(),
return false
}
DialogHelper.showDialog(
this, "提示", "确定退出修改?已编辑的内容将丢失", "继续编辑", " 退出", cancelClickCallback = { finish() },
this,
"提示",
"确定退出修改?已编辑的内容将丢失",
"继续编辑",
" 退出",
cancelClickCallback = { finish() },
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
return true
@ -847,7 +858,12 @@ class QuestionEditActivity : BaseRichEditorActivity<QuestionEditViewModel>(),
mBinding.forumIconView.visibility = View.VISIBLE
mBinding.forumContainer.background =
ContextCompat.getDrawable(this, com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999)
mBinding.chooseForumTv.setTextColor(ContextCompat.getColor(this, com.gh.gamecenter.common.R.color.text_secondary))
mBinding.chooseForumTv.setTextColor(
ContextCompat.getColor(
this,
com.gh.gamecenter.common.R.color.text_secondary
)
)
}
private fun setSectionUI(isEmpty: Boolean) {

View File

@ -13,6 +13,7 @@ import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.video.poster.video.VideoPosterFragment
/**
* 发视频
@ -22,6 +23,7 @@ class VideoPublishActivity : ToolBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
VideoPosterFragment.clearVideoCoverCache(this)
updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface)
mToolbar.navigationIcon = null
findViewById<View>(R.id.backBtn).setOnClickListener { onBackPressed() }

View File

@ -23,20 +23,24 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.common.entity.NotificationUgc
import com.gh.gamecenter.common.mvvm.Status
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.FragmentVideoPublishBinding
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.entity.ActivityLabelEntity
import com.gh.gamecenter.entity.ForumDetailEntity
import com.gh.gamecenter.entity.VideoDraftEntity
import com.gh.gamecenter.entity.VideoEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment
@ -44,12 +48,12 @@ import com.gh.gamecenter.qa.dialog.ChooseForumActivity
import com.gh.gamecenter.qa.dialog.ChooseSectionDialogFragment
import com.gh.gamecenter.qa.dialog.InputUrlDialogFragment
import com.gh.gamecenter.qa.editor.GameActivity
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.utils.Util_System_Keyboard
import com.therouter.TheRouter
import java.io.File
class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
@ -661,7 +665,8 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
private fun setForumUI() {
mBinding.forumIconView.visibility = View.VISIBLE
mBinding.forumContainer.background = com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999.toDrawable(requireContext())
mBinding.forumContainer.background =
com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999.toDrawable(requireContext())
mBinding.chooseForumTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext()))
}
@ -757,29 +762,24 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
private fun startMediaStore() {
try {
PermissionHelper.checkStoragePermissionBeforeAction(requireContext()) {
var intent: Intent? = null
val postcard = TheRouter.build(RouteConsts.activity.videoCoverEditActivity)
when {
mVideoFileEntity?.url?.isNotEmpty() == true -> {
val videoEntity = VideoEntity(
length = mVideoFileEntity?.length ?: 0, url = mVideoFileEntity?.url ?: ""
)
intent = PosterEditActivity.getIntentByVideo(
requireContext(),
videoEntity
)
postcard.withParcelable(EntranceConsts.KEY_VIDEO_ENTITY, videoEntity)
}
mVideoFileEntity?.path?.isNotEmpty() == true -> {
intent = PosterEditActivity.getIntentByPath(
requireContext(), mVideoFileEntity?.url ?: ""
)
postcard.withString(EntranceConsts.KEY_PATH_VIDEO, mVideoFileEntity?.url ?: "")
}
else -> {
throwExceptionInDebug("video not found")
}
}
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
postcard.navigation(requireActivity(), REQUEST_CODE_IMAGE_CROP)
}
} catch (e: Exception) {
toast(R.string.media_image_hint)
@ -966,9 +966,11 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
super.onDarkModeChanged()
setSectionUI(mViewModel.selectSection == null)
mBinding.activityTv.setTextColor(
if (mViewModel.selectActivityLabelEntity != null) com.gh.gamecenter.common.R.color.text_FA8500.toColor(requireContext()) else com.gh.gamecenter.common.R.color.text_primary.toColor(
requireContext()
)
if (mViewModel.selectActivityLabelEntity != null) {
com.gh.gamecenter.common.R.color.text_FA8500.toColor(requireContext())
} else {
com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext())
}
)
mBinding.forumContainer.background =
if (mViewModel.communityEntity == null) R.drawable.button_round_primary_light.toDrawable(requireContext()) else com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999.toDrawable(

View File

@ -1,231 +1,34 @@
package com.gh.gamecenter.video.poster
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.PopupWindow
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.BaseActivity_TabLayout
import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.ActivityPosterEditBinding
import com.gh.gamecenter.entity.VideoEntity
import com.gh.gamecenter.video.poster.photo.PhotoAlbumsAdapter
import com.gh.gamecenter.video.poster.photo.PhotoAlbumsSpanner
import com.gh.gamecenter.video.poster.photo.PhotoPosterFragment
import com.gh.gamecenter.video.poster.video.VideoPosterFragment
import com.gh.gamecenter.video.upload.view.UploadVideoActivity
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 java.io.File
import com.therouter.TheRouter
import com.therouter.router.Route
class PosterEditActivity : BaseActivity_TabLayout(), AdapterView.OnItemSelectedListener,
AlbumCollection.AlbumCallbacks {
private lateinit var mBinding: ActivityPosterEditBinding
private val mAlbumCollection = AlbumCollection()
private lateinit var mAlbumsSpinner: PhotoAlbumsSpanner
private lateinit var mAlbumsAdapter: PhotoAlbumsAdapter
private lateinit var mPhotoPosterFragment: PhotoPosterFragment
private lateinit var mVideoPosterFragment: VideoPosterFragment
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (UploadVideoActivity.REQUEST_CODE_IMAGE_CROP == requestCode && resultCode == Activity.RESULT_OK) {
setResult(Activity.RESULT_OK, data)
finish()
}
}
override fun provideTabView(position: Int, tabTitle: String?): View {
val tabCustomView = BaseFragment_TabLayout.createDefaultTabCustomView(this, tabTitle)
tabCustomView.findViewById<TextView>(R.id.tab_title)
.setTextColor(resources.getColorStateList(R.color.poster_text_tabbar_style))
return tabCustomView
}
override fun initFragmentList(fragments: MutableList<Fragment>) {
val videoPath = intent.getStringExtra(EntranceConsts.KEY_PATH_VIDEO)
val videoEntity = intent.getParcelableExtra<VideoEntity>(VideoEntity::class.java.simpleName)
mPhotoPosterFragment = PhotoPosterFragment()
mVideoPosterFragment = VideoPosterFragment.newInstance(videoPath, videoEntity)
fragments.add(mVideoPosterFragment)
fragments.add(mPhotoPosterFragment)
}
override fun initTabTitleList(tabTitleList: MutableList<String>) {
tabTitleList.add("视频截图")
tabTitleList.add("相册选择")
}
@Route(path = RouteConsts.activity.videoCoverEditActivity)
class PosterEditActivity : BaseActivity() {
override fun getLayoutId(): Int {
return R.layout.activity_poster_edit
return R.layout.actvitity_video_cover_edit
}
override fun onCreate(savedInstanceState: Bundle?) {
TheRouter.inject(this)
super.onCreate(savedInstanceState)
Matisse.from(this).choose(MimeType.ofImage()).showSingleMediaType(true)
DisplayUtils.setLightStatusBar(this, false)
setStatusBarColor(Color.TRANSPARENT)
setNavigationTitle("编辑封面")
initView()
initAlbumsSpinner()
}
private fun initView() {
mBinding = ActivityPosterEditBinding.bind(mContentView)
mBinding.activityViewPager.setScrollable(false)
mBinding.activityViewPager.addOnPageChangeListener {
if (VIDEO_POSTER_INDEX == it) {
setNavigationTitle("编辑封面")
mTitleTv.removeDrawable()
} else {
setPhotoNavigationTitle(false)
}
}
mBinding.confirm.setOnClickListener {
val selectImagePath = getSelectImagePath()
if (selectImagePath != null) {
if (mBinding.activityViewPager.currentItem == 0) {
val data = Intent()
data.putExtra(CropImageActivity.RESULT_CLIP_PATH, selectImagePath)
setResult(Activity.RESULT_OK, data)
finish()
} else {
val intent = CropImageActivity.getIntent(this, selectImagePath, 9 / 16F, mEntrance)
startActivityForResult(intent, UploadVideoActivity.REQUEST_CODE_IMAGE_CROP)
}
} else {
toast("请选择图片")
}
}
mTitleTv.setOnClickListener {
setPhotoNavigationTitle(true)
mAlbumsSpinner.show(mBinding.activityViewPager.height)
}
DisplayUtils.transparentStatusBar(this)
mBaseHandler.postDelayed({
mBinding.activityTabIndicator.generatePath(
mBinding.activityViewPager.currentItem,
0F
)
}, 10)
}
private fun initAlbumsSpinner() {
mAlbumsAdapter = PhotoAlbumsAdapter(this)
mAlbumsSpinner = PhotoAlbumsSpanner(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 (mPhotoPosterFragment.isAdded) {
mPhotoPosterFragment.loadPhotos(album)
}
}
}
mAlbumsSpinner.setDismissListener(PopupWindow.OnDismissListener {
setPhotoNavigationTitle(false)
})
mAlbumCollection.onCreate(this, this)
mAlbumCollection.loadAlbums()
}
private fun setPhotoNavigationTitle(up: Boolean) {
if (mBinding.activityViewPager.currentItem == VIDEO_POSTER_INDEX) return
mTitleTv.setDrawableEnd(if (up) R.drawable.poster_select_up else R.drawable.poster_select_down)
mTitleTv.compoundDrawablePadding = 2F.dip2px()
mTitleTv.text = "全部图片"
}
private fun getSelectImagePath(): String? {
if (mBinding.activityViewPager.currentItem == VIDEO_POSTER_INDEX) {
val clipPath = cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg"
mVideoPosterFragment.savePicture(clipPath)
return clipPath
} else {
return mPhotoPosterFragment.getSelectImagePath()
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
}
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 (::mPhotoPosterFragment.isInitialized && mPhotoPosterFragment.isAdded) {
mPhotoPosterFragment.loadPhotos(album)
}
}
}
override fun onAlbumReset() {
}
override fun provideNavigationIcon(): Int {
return com.gh.gamecenter.common.R.drawable.ic_toolbar_back_white
}
override fun handleBackPressed(): Boolean {
DialogHelper.showDialog(this, "提示", "确定放弃编辑封面吗?", "确定", "暂不", { finish() })
return true
}
companion object {
private const val VIDEO_POSTER_INDEX = 0
private const val PHOTO_POSTER_INDEX = 1
fun getIntentByPath(context: Context, videoPath: String): Intent {
val intent = Intent(context, PosterEditActivity::class.java)
intent.putExtra(EntranceConsts.KEY_PATH_VIDEO, videoPath)
return intent
}
fun getIntentByVideo(context: Context, videoEntity: VideoEntity): Intent {
val intent = Intent(context, PosterEditActivity::class.java)
intent.putExtra(VideoEntity::class.java.simpleName, videoEntity)
return intent
}
val tag = VideoPosterFragment::class.java.name
val fragment = supportFragmentManager.findFragmentByTag(tag) ?: VideoPosterFragment.newInstance()
fragment.arguments = intent.extras
supportFragmentManager
.beginTransaction()
.replace(R.id.fcv_container, fragment, tag)
.commitNowAllowingStateLoss()
}
}

View File

@ -0,0 +1,91 @@
package com.gh.gamecenter.video.poster
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.gh.gamecenter.common.utils.dip2px
class PreviewMaskView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) :
View(context, attrs, defStyleAttr, defStyleRes) {
private var isLight = false
private var leftTopCorner = 0F
private var rightTopCorner = 0F
private var leftBottomCorner = 0F
private var rightBottomCorner = 0F
private val paint by lazy {
Paint().apply {
isAntiAlias = true
isDither = true
}
}
val defaultColor = Color.parseColor("#40000000")
val lightColor = Color.parseColor("#000000")
private val xFermode by lazy {
PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private val rectF = RectF()
private var path = Path()
fun setCorners(leftTop: Float, rightTop: Float, rightBottom: Float, leftBottom: Float) {
leftTopCorner = leftTop
rightTopCorner = rightTop
rightBottomCorner = rightBottom
leftBottomCorner = leftBottom
postInvalidateOnAnimation()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
rectF.set(0F, 0F, w.toFloat(), h.toFloat())
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (isLight) {
paint.color = lightColor
val layerId = canvas.saveLayerAlpha(rectF, 64)
drawBackground(canvas)
paint.xfermode = xFermode
canvas.drawRoundRect(rectF, 4F.dip2px().toFloat(), 4F.dip2px().toFloat(), paint)
paint.xfermode = null
canvas.restoreToCount(layerId)
} else {
paint.color = defaultColor
drawBackground(canvas)
}
}
private fun drawBackground(canvas: Canvas) {
path.reset()
val radii = floatArrayOf(
leftTopCorner,
leftTopCorner,
rightTopCorner,
rightTopCorner,
rightBottomCorner,
rightBottomCorner,
leftBottomCorner,
leftBottomCorner,
)
path.addRoundRect(rectF, radii, Path.Direction.CW)
canvas.drawPath(path, paint)
}
fun setLight(isLight: Boolean) {
this.isLight = isLight
postInvalidateOnAnimation()
}
}

View File

@ -1,75 +1,282 @@
package com.gh.gamecenter.video.poster.video
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContract
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.GridLayoutManager
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.FragmentVideoPosterBinding
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.PermissionHelper.checkStoragePermissionBeforeAction
import com.gh.gamecenter.common.view.cropbox.CropBoxStyle
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.FragmentVideoCoverEditBinding
import com.gh.gamecenter.entity.VideoEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.video.poster.video.VideoPosterViewModel.Companion.SELECTED_IMAGE_POSITION
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.ShapeAppearanceModel
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import java.io.File
import java.io.FileOutputStream
class VideoPosterFragment : BaseFragment<Any>() {
private lateinit var mBinding: FragmentVideoPosterBinding
private lateinit var mAdapter: VideoPosterReviewAdapter
private lateinit var binding: FragmentVideoCoverEditBinding
private lateinit var viewModel: VideoPosterViewModel
private lateinit var mViewModel: VideoPosterViewModel
var videoPath: String? = null
override fun getLayoutId(): Int {
return R.layout.fragment_video_poster
var videoEntity: VideoEntity? = null
private lateinit var adapter: VideoPosterReviewAdapter
private lateinit var takePhotoLauncher: ActivityResultLauncher<Unit>
override fun getInflatedLayout(): View {
return View(requireContext())
}
override fun getLayoutId(): Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
videoPath = arguments?.getString(EntranceConsts.KEY_PATH_VIDEO)
videoEntity = arguments?.getParcelable<VideoEntity>(EntranceConsts.KEY_VIDEO_ENTITY)
super.onCreate(savedInstanceState)
mBinding = FragmentVideoPosterBinding.bind(mCachedView)
val videoPath = arguments?.getString(EntranceConsts.KEY_PATH_VIDEO)
val videoEntity = arguments?.getParcelable<VideoEntity>(VideoEntity::class.java.simpleName)
val videoCoverCache =
SPUtils.getString(Constants.SP_VIDEO_COVER_CACHE).toObject<VideoPosterViewModel.VideoCoverCache>()
val factory =
VideoPosterViewModel.Factory(HaloApp.getInstance().application, videoPath, videoEntity, videoCoverCache)
viewModel = ViewModelProviders.of(this, factory).get(VideoPosterViewModel::class.java)
val factory = VideoPosterViewModel.Factory(HaloApp.getInstance().application, videoPath, videoEntity)
mViewModel = ViewModelProviders.of(this, factory).get(VideoPosterViewModel::class.java)
mViewModel.videoPreviewsLiveData.observeNonNull(this, callback = {
mAdapter.submitList(it)
})
mViewModel.previewLiveData.observeNonNull(this, callback = {
if (it.thumbs != null) {
mBinding.receiveIv.setBitmap(it.thumbs)
} else if (it.thumbsUrl != null) {
ImageUtils.picasso
.load(Uri.parse(it.thumbsUrl))
.into(mBinding.receiveIv.cropImageZoomView)
takePhotoLauncher = registerForActivityResult(object : ActivityResultContract<Unit, Intent?>() {
override fun createIntent(context: Context, input: Unit): Intent {
return LocalMediaActivity.getIntent(
context,
ChooseType.IMAGE,
1,
"视频封面编辑"
)
}
})
override fun parseResult(resultCode: Int, intent: Intent?): Intent? {
return intent
}
}) {
if (it != null) {
val uris = Matisse.obtainResult(it)
if (uris.isNotEmpty()) {
val path = PathUtils.getPath(requireContext(), uris.first())
if (path != null) {
val ivZoom = binding.cropImageCustom.cropImageZoomView
val bitmap = BitmapUtils.getBitmapByFile(path, ivZoom.width, ivZoom.height)
if (bitmap != null) {
val videoPreview = VideoPosterViewModel.VideoPreview(0, bitmap, null)
viewModel.setSelectedImage(videoPreview)
}
}
}
}
}
}
override fun initView(view: View?) = Unit
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return FragmentVideoCoverEditBinding.inflate(inflater, container, false)
.also {
binding = it
}.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mBinding.receiveIv.setCropRatio(9 / 16F)
mBinding.reviewList.layoutManager = GridLayoutManager(requireContext(), 10)
mAdapter = VideoPosterReviewAdapter(requireContext(), mViewModel)
mBinding.reviewList.adapter = mAdapter
adapter = VideoPosterReviewAdapter(requireContext(), viewModel)
binding.rvImages.layoutManager = GridLayoutManager(requireContext(), 10)
binding.rvImages.adapter = adapter
binding.ivTakePhoto.shapeAppearanceModel = ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
.build()
binding.ivTakePhoto.clipToOutline = true
binding.ivPreviewBorder.shapeAppearanceModel = ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
.build()
binding.ivPreviewBorder.strokeWidth = 4F.dip2px().toFloat()
binding.ivPreviewBorder.strokeColor = ColorStateList.valueOf(Color.parseColor("#2496FF"))
binding.ivPreviewBorder.clipToOutline = true
binding.cropImageCustom.setCropBoxStyle(CropBoxStyle.Rectangle(23F / 41))
binding.ivTakePhoto.setOnClickListener {
val videoPreview = viewModel.selectedImage.value
if (videoPreview == null) {
checkStoragePermissionBeforeAction(requireContext()) { takePhotoLauncher.launch(Unit) }
} else {
viewModel.setPreviewImage(SELECTED_IMAGE_POSITION, videoPreview)
}
}
binding.ivRemove.setOnClickListener {
viewModel.setSelectedImage(null)
if (viewModel.previewPosition.value == SELECTED_IMAGE_POSITION) {
adapter.getItem(0)?.let {
viewModel.setPreviewImage(0, it)
}
}
}
binding.tvCancel.setOnClickListener {
activity?.finish()
}
binding.tvSubmit.setOnClickListener {
activity?.let {
val clipPath = it.cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg"
binding.cropImageCustom.savePicture(clipPath)
saveVideoOriginalCoverToLocal(it)
val data = Intent()
data.putExtra(CropImageActivity.RESULT_CLIP_PATH, clipPath)
it.setResult(Activity.RESULT_OK, data)
it.finish()
}
}
with(viewModel) {
videoPreviewsLiveData.observeNonNull(viewLifecycleOwner, callback = {
adapter.submitList(it)
})
previewLiveData.observeNonNull(viewLifecycleOwner, callback = {
binding.cropImageCustom.reset()
it.values?.let(binding.cropImageCustom::setTransformationValues)
if (it.thumbs != null) {
binding.cropImageCustom.setBitmap(it.thumbs)
} else if (it.thumbsUrl != null) {
ImageUtils.picasso
.load(Uri.parse(it.thumbsUrl))
.into(binding.cropImageCustom.cropImageZoomView)
}
})
selectedImage.observe(viewLifecycleOwner) {
if (it == null) {
binding.ivTakePhoto.setImageResource(R.drawable.ic_video_cover_edit_take_photo)
} else {
binding.ivTakePhoto.setImageBitmap(it.thumbs)
}
binding.ivRemove.goneIf(it == null)
}
previewPosition.observe(viewLifecycleOwner) {
setSelectedImageSelected(it == SELECTED_IMAGE_POSITION)
adapter.setPreviewPosition(it)
}
}
}
fun savePicture(path: String) {
if (isAdded) throwExceptionInDebug("save clip picture failure", !mBinding.receiveIv.savePicture(path))
/**
* 将封面原图保存在本地,以便下次编辑时显示
*/
private fun saveVideoOriginalCoverToLocal(context: Context) {
val preview = viewModel.previewLiveData.value
if (preview != null) {
val position = viewModel.previewPosition.value ?: -2
var cache: VideoPosterViewModel.VideoCoverCache? = null
val values = binding.cropImageCustom.transformationValues
if (preview.thumbs != null) {
val path = saveToInternalStorage(context, preview.thumbs)
if (path != null) {
cache = VideoPosterViewModel.VideoCoverCache(position, localPath = path, values = values)
}
} else if (preview.thumbsUrl != null) {
cache = VideoPosterViewModel.VideoCoverCache(position, url = preview.thumbsUrl, values = values)
}
val json = cache?.toJson()
if (json != null) {
SPUtils.setString(Constants.SP_VIDEO_COVER_CACHE, json)
}
}
}
private fun setSelectedImageSelected(isSelected: Boolean) {
if (isSelected) {
binding.ivTakePhoto.strokeWidth = 6F.dip2px().toFloat()
binding.ivTakePhoto.strokeColor = ColorStateList.valueOf(Color.parseColor("#232323"))
} else {
binding.ivTakePhoto.strokeWidth = 0F
}
binding.ivPreviewBorder.goneIf(!isSelected)
}
// 保存到内部存储
private fun saveToInternalStorage(
context: Context,
bitmap: Bitmap
): String? {
try {
val file = createVideoCoverFile(context)
// 将图片保存到文件
FileOutputStream(file).use { fos ->
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
fos.flush()
}
return file.absolutePath
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
companion object {
fun newInstance(videoPath: String?, videoEntity: VideoEntity?): VideoPosterFragment {
val fragment = VideoPosterFragment()
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_PATH_VIDEO, videoPath)
bundle.putParcelable(VideoEntity::class.java.simpleName, videoEntity)
fragment.arguments = bundle
return fragment
private const val VIDEO_COVER_DIRECTORY_NAME = "videoCover"
fun createVideoCoverFile(context: Context): File {
// 获取应用私有目录
val directory = context.getDir(VIDEO_COVER_DIRECTORY_NAME, Context.MODE_PRIVATE)
// 创建文件
return File(directory, "cover_image.jpg")
}
/**
* 清理上次裁剪图片留下的缓存文件
*/
fun clearVideoCoverCache(context: Context) {
SPUtils.setString(Constants.SP_VIDEO_COVER_CACHE, "")
context.getDir(VIDEO_COVER_DIRECTORY_NAME, Context.MODE_PRIVATE).let { directory ->
directory.listFiles()?.forEach { it.delete() }
}
}
fun newInstance() = VideoPosterFragment()
}
}

View File

@ -1,15 +1,19 @@
package com.gh.gamecenter.video.poster.video
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.net.Uri
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.databinding.VideoPosterPreviewItemBinding
import com.gh.gamecenter.video.poster.video.VideoPosterViewModel.Companion.SELECTED_IMAGE_POSITION
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.ShapeAppearanceModel
import com.lightgame.adapter.BaseRecyclerAdapter
class VideoPosterReviewAdapter(
@ -19,11 +23,7 @@ class VideoPosterReviewAdapter(
private var mListData: MutableList<VideoPosterViewModel.VideoPreview> = ArrayList()
private val mSelectMap = HashMap<Int, Boolean>()
init {
mSelectMap[0] = true // default select
}
private var selectedPosition = 0
fun submitList(listData: List<VideoPosterViewModel.VideoPreview>) {
mListData = listData.toMutableList()
@ -41,6 +41,22 @@ class VideoPosterReviewAdapter(
override fun onBindViewHolder(holder: VideoPosterReviewItemViewHolder, position: Int) {
val builder = ShapeAppearanceModel().toBuilder()
when (position) {
0 -> builder.setTopLeftCorner(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
.setTopRightCorner(CornerFamily.ROUNDED, 0F)
.setBottomRightCorner(CornerFamily.ROUNDED, 0F)
.setBottomLeftCorner(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
itemCount - 1 -> builder.setTopLeftCorner(CornerFamily.ROUNDED, 0F)
.setTopRightCorner(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
.setBottomRightCorner(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
.setBottomLeftCorner(CornerFamily.ROUNDED, 0F)
else -> builder.setAllCorners(CornerFamily.ROUNDED, 0F)
}
holder.binding.preview.shapeAppearanceModel = builder.build()
val videoPreview = mListData[position]
val thumbs = videoPreview.thumbs
if (thumbs != null) {
@ -51,41 +67,67 @@ class VideoPosterReviewAdapter(
.into(holder.binding.preview)
}
holder.binding.preview.setOnClickListener {
viewModel.previewLiveData.postValue(videoPreview)
viewModel.setPreviewImage(position, mListData[position])
}
setSelected(holder, position)
mSelectMap[position] = true
for (entry in mSelectMap) {
if (entry.value && entry.key != position) {
mSelectMap[entry.key] = false
notifyItemChanged(entry.key)
}
}
override fun onBindViewHolder(holder: VideoPosterReviewItemViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.contains(PAYLOADS_IS_SELECTED)) {
setSelected(holder, position)
} else {
super.onBindViewHolder(holder, position, payloads)
}
}
private fun setSelected(holder: VideoPosterReviewItemViewHolder, position: Int) {
holder.binding.preview.clipToOutline = true
holder.binding.previewBorderInner.clipToOutline = true
holder.binding.previewBorderOut.shapeAppearanceModel = ShapeAppearanceModel().toBuilder()
.setAllCorners(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
.build()
holder.binding.previewBorderOut.strokeColor = ColorStateList.valueOf(Color.parseColor("#232323"))
holder.binding.previewBorderOut.strokeWidth = 6F.dip2px().toFloat()
holder.binding.previewBorderOut.clipToOutline = true
holder.binding.previewBorderInner.shapeAppearanceModel = ShapeAppearanceModel().toBuilder()
.setAllCorners(CornerFamily.ROUNDED, 4F.dip2px().toFloat())
.build()
holder.binding.previewBorderInner.strokeColor = ColorStateList.valueOf(Color.parseColor("#2496FF"))
holder.binding.previewBorderInner.strokeWidth = 4F.dip2px().toFloat()
holder.binding.gPreviewBorder.goneIf(selectedPosition != position)
fun getCorner(hasCorner: Boolean) = if (hasCorner) 4F.dip2px().toFloat() else 0F
val leftCorner = getCorner(position == 0)
val rightCorner = getCorner(position == itemCount - 1)
holder.binding.vCover.setCorners(
leftCorner,
rightCorner,
rightCorner,
leftCorner,
)
holder.binding.vCover.setLight(selectedPosition == position)
}
fun setPreviewPosition(position: Int) {
if (selectedPosition != position) {
val lastPosition = selectedPosition
selectedPosition = position
if (lastPosition != SELECTED_IMAGE_POSITION) {
notifyItemChanged(lastPosition, PAYLOADS_IS_SELECTED)
}
if (selectedPosition != SELECTED_IMAGE_POSITION) {
notifyItemChanged(position, PAYLOADS_IS_SELECTED)
}
notifyItemChanged(position)
}
}
holder.binding.previewContainer.radius = if (mSelectMap[position] == true) {
3F.dip2px().toFloat()
} else if (position == 0 || position == itemCount - 1) {
4F.dip2px().toFloat()
} else {
0F
}
holder.binding.previewBorder.visibility = if (mSelectMap[position] == true) {
View.VISIBLE
} else View.GONE
fun getItem(position: Int) = mListData.getOrNull(position)
val previewContainerLp = holder.binding.previewContainer.layoutParams as ConstraintLayout.LayoutParams
if (position == 0) {
previewContainerLp.rightMargin = (-4F).dip2px()
previewContainerLp.leftMargin = 0
} else if (position == itemCount - 1) {
previewContainerLp.leftMargin = (-4F).dip2px()
previewContainerLp.rightMargin = 0
} else {
previewContainerLp.rightMargin = 0
previewContainerLp.leftMargin = 0
}
holder.binding.previewContainer.layoutParams = previewContainerLp
companion object {
private const val PAYLOADS_IS_SELECTED = "payloads_is_selected"
}
}

View File

@ -4,9 +4,11 @@ import android.app.Application
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import androidx.lifecycle.*
import com.gh.gamecenter.common.utils.BitmapUtils
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.common.view.CropImageZoomView.TransformationValues
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.entity.VideoEntity
import java.io.File
@ -15,13 +17,33 @@ import kotlin.math.roundToLong
class VideoPosterViewModel(
application: Application,
val videoPath: String?,
val videoEntity: VideoEntity?
val videoEntity: VideoEntity?,
val cache: VideoCoverCache?
) : AndroidViewModel(application) {
val videoPreviewsLiveData = MutableLiveData<List<VideoPreview>>()
val previewLiveData = MutableLiveData<VideoPreview>()
private val _selectedImage = MutableLiveData<VideoPreview?>()
val selectedImage: LiveData<VideoPreview?> = _selectedImage
fun setSelectedImage(videoPreview: VideoPreview?) {
_selectedImage.value = videoPreview
if (videoPreview != null) {
setPreviewImage(SELECTED_IMAGE_POSITION, videoPreview)
}
}
private val _previewPosition = MutableLiveData<Int>()
val previewPosition: LiveData<Int> = _previewPosition
fun setPreviewImage(position: Int, videoPreview: VideoPreview) {
val oldPosition = _previewPosition.value
if (oldPosition != position) {
_previewPosition.value = position
previewLiveData.value = videoPreview
}
}
init {
if (videoPath != null) {
@ -36,22 +58,35 @@ class VideoPosterViewModel(
}
val videoReviews = ArrayList<VideoPreview>()
val cachePreview = cache?.toVideoPreview()
for (i in 0 until totalThumbsCount) {
val frameTime: Long = (startPosition + interval * i).roundToLong()
val thumbsUrl = ImageUtils.getVideoSnapshot(videoEntity.url, frameTime * 1000)
val element = VideoPreview(frameTime, null, thumbsUrl)
videoReviews.add(element)
if (videoReviews.size == 1) {
if (cachePreview == null && videoReviews.size == 1) {
previewLiveData.postValue(element)
}
}
videoPreviewsLiveData.postValue(videoReviews)
cachePreview?.let(::initFromCache)
} else {
throwExceptionInDebug("video not found")
}
}
private fun initFromCache(cachePreview: VideoPreview) {
cache ?: return
previewLiveData.postValue(cachePreview)
_previewPosition.postValue(cache.position)
if (cache.position == SELECTED_IMAGE_POSITION) {
_selectedImage.value = cachePreview
}
}
private fun getImagesByVideo() {
throwExceptionInDebug("video file not found", !File(videoPath).exists())
@ -68,6 +103,7 @@ class VideoPosterViewModel(
return
}
val cachePreview = cache?.toVideoPreview()
val duration = durationString.toLong()
val interval = (duration / totalThumbsCount)
val videoReviews = ArrayList<VideoPreview>()
@ -88,23 +124,64 @@ class VideoPosterViewModel(
val element = VideoPreview(frameTime * 1000, bitmap)
videoReviews.add(element)
videoPreviewsLiveData.postValue(videoReviews)
if (videoReviews.size == 1) {
if (cachePreview == null && videoReviews.size == 1) {
previewLiveData.postValue(element)
}
}
cachePreview?.let(::initFromCache)
mediaMetadataRetriever.release()
}
class VideoPreview(val time: Long, val thumbs: Bitmap? = null, val thumbsUrl: String? = null)
companion object {
const val SELECTED_IMAGE_POSITION = -1
}
class VideoPreview(
val time: Long,
val thumbs: Bitmap? = null,
val thumbsUrl: String? = null,
val values: TransformationValues? = null
)
class VideoCoverCache(
val position: Int,
val localPath: String? = null,
val url: String? = null,
val values: TransformationValues
) {
fun toVideoPreview(): VideoPreview? =
when {
localPath != null -> {
if (File(localPath).exists()) {
val bitmap = BitmapUtils.getBitmapByFile(localPath, Bitmap.Config.RGB_565)
if (bitmap != null) {
VideoPreview(0, bitmap, null, values)
} else {
null
}
} else {
null
}
}
url != null -> VideoPreview(0, null, url, values)
else -> null
}
}
class Factory(
private val mApplication: Application,
private val mVideoPath: String?,
private val mVideoEntity: VideoEntity?
private val mVideoEntity: VideoEntity?,
private val cache: VideoCoverCache?
) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return VideoPosterViewModel(mApplication, mVideoPath, mVideoEntity) as T
return VideoPosterViewModel(mApplication, mVideoPath, mVideoEntity, cache) as T
}
}
}

View File

@ -36,6 +36,7 @@ import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.callback.CancelListener
import com.gh.gamecenter.common.callback.ConfirmListener
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.NotificationUgc
import com.gh.gamecenter.common.entity.SimpleGameEntity
import com.gh.gamecenter.common.mvvm.Status
@ -49,12 +50,13 @@ import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.newsdetail.NewsDetailActivity
import com.gh.gamecenter.qa.editor.GameActivity
import com.gh.gamecenter.video.label.VideoLabelActivity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.poster.video.VideoPosterFragment
import com.gh.gamecenter.video.upload.OnUploadListener
import com.gh.gamecenter.video.upload.UploadManager
import com.google.android.flexbox.FlexboxLayout
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.therouter.TheRouter
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import org.json.JSONArray
@ -96,25 +98,6 @@ class UploadVideoActivity : ToolBarActivity() {
mBinding.gameName.text = game.name
mBinding.gameName.setTextColor(resources.getColor(com.gh.gamecenter.common.R.color.text_primary))
}
} else if (requestCode == REQUEST_CODE_IMAGE_STORE && resultCode == Activity.RESULT_OK) {
if (data != null) {
val uris = Matisse.obtainResult(data)
for (uri in uris) {
val picturePath = PathUtils.getPath(application, uri)
if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) {
val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024
val application: Application = application
Utils.toast(getApplication(), application.getString(com.gh.gamecenter.common.R.string.pic_max_hint, count))
continue
}
Utils.log("picturePath = $picturePath")
// skip image crop
val intent = CropImageActivity.getIntent(this@UploadVideoActivity, picturePath, 9 / 16F, mEntrance)
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
break
}
}
} else if (requestCode == REQUEST_CODE_IMAGE_CROP && resultCode == Activity.RESULT_OK) {
if (data != null) {
val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH)
@ -136,6 +119,7 @@ class UploadVideoActivity : ToolBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
VideoPosterFragment.clearVideoCoverCache(this)
mVideoLink = intent.getParcelableExtra(VideoLinkEntity::class.java.simpleName)
mEntranceLink = intent.getStringExtra(EntranceConsts.KEY_ENTRANCE_LINK) ?: ""
mPath = intent.getStringExtra(EntranceConsts.KEY_PATH) ?: "其他"
@ -259,7 +243,8 @@ class UploadVideoActivity : ToolBarActivity() {
R.drawable.ic_upload_video_activity_unenable
)
)
mBinding.chooseActivityIv.background = ContextCompat.getDrawable(this, com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999)
mBinding.chooseActivityIv.background =
ContextCompat.getDrawable(this, com.gh.gamecenter.common.R.drawable.bg_shape_f5_radius_999)
}
}
@ -429,14 +414,7 @@ class UploadVideoActivity : ToolBarActivity() {
private fun startMediaStore() {
try {
PermissionHelper.checkStoragePermissionBeforeAction(this) {
// Matisse.from(this@UploadVideoActivity)
// .choose(MimeType.ofImage())
// .showSingleMediaType(true)
// .countable(true)
// .addFilter(GhMatisseFilter())
// .maxSelectable(1)
// .forResult(REQUEST_CODE_IMAGE_STORE)
val postcard = TheRouter.build(RouteConsts.activity.videoCoverEditActivity)
val videoPath = if (mViewModel.videoDraft != null) {
mViewModel.videoDraft?.localPath
@ -444,22 +422,17 @@ class UploadVideoActivity : ToolBarActivity() {
intent.getStringExtra(EntranceConsts.KEY_PATH_VIDEO)
}
var intent: Intent? = null
if (!videoPath.isNullOrEmpty()) {
intent = PosterEditActivity.getIntentByPath(this@UploadVideoActivity, videoPath)
postcard.withString(EntranceConsts.KEY_PATH_VIDEO, videoPath)
} else {
val videoPatch = mViewModel.videoPatch
if (videoPatch != null) {
intent = PosterEditActivity.getIntentByVideo(this@UploadVideoActivity, videoPatch)
postcard.withParcelable(EntranceConsts.KEY_VIDEO_ENTITY, videoPatch)
} else {
throwExceptionInDebug("video not found")
}
}
if (intent != null) {
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
} else {
toast("找不到相关视频")
}
postcard.navigation(this, REQUEST_CODE_IMAGE_CROP)
}
} catch (e: Exception) {
toast(R.string.media_image_hint)
@ -672,8 +645,16 @@ class UploadVideoActivity : ToolBarActivity() {
flexCell.gravity = Gravity.CENTER
flexCell.text = videoTagEntity.name
flexCell.tag = videoTagEntity.id
flexCell.setTextColor(DrawableView.getSelectorColorStyle(com.gh.gamecenter.common.R.color.text_secondary, com.gh.gamecenter.common.R.color.white))
flexCell.background = DrawableView.getOvalSelectorStyle(com.gh.gamecenter.common.R.color.text_f2f2f2, com.gh.gamecenter.common.R.color.primary_theme)
flexCell.setTextColor(
DrawableView.getSelectorColorStyle(
com.gh.gamecenter.common.R.color.text_secondary,
com.gh.gamecenter.common.R.color.white
)
)
flexCell.background = DrawableView.getOvalSelectorStyle(
com.gh.gamecenter.common.R.color.text_f2f2f2,
com.gh.gamecenter.common.R.color.primary_theme
)
flexCell.setPadding(12F.dip2px(), 0, 12F.dip2px(), 0)
flexCell.setOnClickListener {
Util_System_Keyboard.hideSoftKeyboard(this, mBinding.gameTitle)
@ -789,7 +770,11 @@ class UploadVideoActivity : ToolBarActivity() {
mBinding.uploadSpeed.visibility = View.VISIBLE
mBinding.uploadButton.visibility = View.VISIBLE
mBinding.uploadSpeed.text =
(SpeedUtils.getSpeed(speed) + "预计还需" + SpeedUtils.getRemainTime(totalSize, currentSize, speed))
(SpeedUtils.getSpeed(speed) + "预计还需" + SpeedUtils.getRemainTime(
totalSize,
currentSize,
speed
))
mBinding.uploadProgress.update(((360 * currentSize) / totalSize).toInt(), "")
}
}
@ -941,7 +926,6 @@ class UploadVideoActivity : ToolBarActivity() {
private const val REQUEST_GAME_CODE = 116
const val RESULT_CODE_VIDEO = 117
const val RESULT_CODE_DRAFT = 118
const val REQUEST_CODE_IMAGE_STORE = 119
const val REQUEST_CODE_IMAGE_CROP = 120
const val REQUEST_CODE_CHOOSE_LABEL = 121

View File

@ -18,6 +18,7 @@ import com.gh.gamecenter.common.utils.UploadImageUtils;
import com.gh.gamecenter.CropImageActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.view.cropbox.CropBoxStyle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -68,108 +69,104 @@ public class UserPortraitCropImageActivity extends CropImageActivity {
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.layout_menu_positive) {
final WaitingDialogFragment postDialog = WaitingDialogFragment.newInstance(getString(R.string.post_img));
postDialog.show(getSupportFragmentManager(), null);
final String path = getCacheDir() + File.separator + System.currentTimeMillis() + ".jpg";
Observable.create((ObservableOnSubscribe<String>) emitter -> {
boolean isSuccess = savePicture(path);
if (isSuccess) {
UploadImageUtils.INSTANCE.uploadImage(UploadImageUtils.UploadType.icon, path, new UploadImageUtils.OnUploadImageListener() {
@Override
public void onSuccess(@NotNull String imageUrl) {
emitter.onNext(imageUrl);
emitter.onComplete();
}
@Override
public void onError(@Nullable Throwable e) {
if (e != null) {
emitter.onError(e);
} else {
emitter.onError(new IllegalStateException("upload image error"));
}
}
@Override
public void onProgress(long total, long progress) {
int percent = (int) (100 * (progress / (float) total));
if (percent >= 100) percent = 99;
if (postDialog != null) {
postDialog.uploadWaitingHint("图片上传中 " + percent + "%");
}
}
});
}
}).subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Response<String>() {
@Override
public void onResponse(String url) {
try {
if (postDialog != null) postDialog.dismissAllowingStateLoss();
mBaseHandler.sendEmptyMessage(0);
String iconCount = sp.getString("updateIconCount", null);
long l = System.currentTimeMillis();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
String time = format.format(new Date(l));
JSONObject jsonObject = new JSONObject();
jsonObject.put("time", time);
if (TextUtils.isEmpty(iconCount)) {
jsonObject.put("count", 1);
} else {
JSONObject json = new JSONObject(iconCount);
String lastTime = json.getString("time");
if (lastTime.equals(time)) {
jsonObject.put("count", json.getInt("count") + 1);
} else {
jsonObject.put("count", 1);
}
}
sp.edit().putString("updateIconCount", jsonObject.toString()).apply();
Intent data = new Intent();
data.putExtra(EntranceConsts.KEY_URL, url);
setResult(RESULT_OK, data);
finish();
} catch (Exception e) {
e.printStackTrace();
protected void saveImage() {
final WaitingDialogFragment postDialog = WaitingDialogFragment.newInstance(getString(R.string.post_img));
postDialog.show(getSupportFragmentManager(), null);
final String path = getCacheDir() + File.separator + System.currentTimeMillis() + ".jpg";
Observable.create((ObservableOnSubscribe<String>) emitter -> {
boolean isSuccess = savePicture(path);
if (isSuccess) {
UploadImageUtils.INSTANCE.uploadImage(UploadImageUtils.UploadType.icon, path, new UploadImageUtils.OnUploadImageListener() {
@Override
public void onSuccess(@NotNull String imageUrl) {
emitter.onNext(imageUrl);
emitter.onComplete();
}
}
@Override
public void onFailure(HttpException e) {
@Override
public void onError(@Nullable Throwable e) {
if (e != null) {
emitter.onError(e);
} else {
emitter.onError(new IllegalStateException("upload image error"));
}
}
@Override
public void onProgress(long total, long progress) {
int percent = (int) (100 * (progress / (float) total));
if (percent >= 100) percent = 99;
if (postDialog != null) {
postDialog.uploadWaitingHint("图片上传中 " + percent + "%");
}
}
});
}
}).subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Response<String>() {
@Override
public void onResponse(String url) {
try {
if (postDialog != null) postDialog.dismissAllowingStateLoss();
try {
if (e != null && e.code() == HttpURLConnection.HTTP_FORBIDDEN && e.response().errorBody() != null) {
JSONObject object = new JSONObject(e.response().errorBody().string());
String detail = object.getString("detail");
if ("too frequent".equals(detail)) {
mBaseHandler.sendEmptyMessage(2);
} else if ("INVALID PICTURE".equals(detail)) {
mBaseHandler.sendEmptyMessage(3);
} else {
mBaseHandler.sendEmptyMessage(1);
}
mBaseHandler.sendEmptyMessage(0);
String iconCount = sp.getString("updateIconCount", null);
long l = System.currentTimeMillis();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
String time = format.format(new Date(l));
JSONObject jsonObject = new JSONObject();
jsonObject.put("time", time);
if (TextUtils.isEmpty(iconCount)) {
jsonObject.put("count", 1);
} else {
JSONObject json = new JSONObject(iconCount);
String lastTime = json.getString("time");
if (lastTime.equals(time)) {
jsonObject.put("count", json.getInt("count") + 1);
} else {
jsonObject.put("count", 1);
}
}
sp.edit().putString("updateIconCount", jsonObject.toString()).apply();
Intent data = new Intent();
data.putExtra(EntranceConsts.KEY_URL, url);
setResult(RESULT_OK, data);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(HttpException e) {
if (postDialog != null) postDialog.dismissAllowingStateLoss();
try {
if (e != null && e.code() == HttpURLConnection.HTTP_FORBIDDEN && e.response().errorBody() != null) {
JSONObject object = new JSONObject(e.response().errorBody().string());
String detail = object.getString("detail");
if ("too frequent".equals(detail)) {
mBaseHandler.sendEmptyMessage(2);
} else if ("INVALID PICTURE".equals(detail)) {
mBaseHandler.sendEmptyMessage(3);
} else {
mBaseHandler.sendEmptyMessage(1);
}
} catch (Exception e1) {
e1.printStackTrace();
} else {
mBaseHandler.sendEmptyMessage(1);
}
} catch (Exception e1) {
e1.printStackTrace();
mBaseHandler.sendEmptyMessage(1);
}
});
}
});
return false;
}
return super.onMenuItemClick(item);
}
// 用户头像压缩规则
@ -188,11 +185,11 @@ public class UserPortraitCropImageActivity extends CropImageActivity {
}
@NonNull
public static Intent getIntent(Context context, String picturePath, float cropRatio, String entrance) {
public static Intent getIntent(Context context, String picturePath, String entrance) {
Intent intent = new Intent(context, UserPortraitCropImageActivity.class);
intent.putExtra(EntranceConsts.KEY_PATH, picturePath);
intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance);
intent.putExtra(EntranceConsts.KEY_IMAGE_CROP_RATIO, cropRatio);
intent.putExtra(EntranceConsts.KEY_IMAGE_CROP_STYLE, CropBoxStyle.Circle.INSTANCE);
return intent;
}
}

View File

@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import com.gh.common.provider.CropImageProviderImpl.Companion.IMAGE_TYPE_AVATAR
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
@ -93,23 +94,13 @@ class ChangeAvatarDialog : BaseDialogFragment() {
if (resultCode == Activity.RESULT_OK && data != null) {
when (requestCode) {
REQUEST_MEDIA_ICON -> {
val selectedPaths = Matisse.obtainResult(data) ?: return
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
Utils.log("picturePath = $picturePath")
// 上传头像
val intent = UserPortraitCropImageActivity.getIntent(
context,
picturePath, 1F, "我的光环(选择头像)"
)
startActivityForResult(intent, REQUEST_CROP_ICON)
}
REQUEST_CROP_ICON -> {
if (data.extras != null) {
val url = data.extras!!.getString(EntranceConsts.KEY_URL)
mUserViewModel?.changeUserInfo(url, UserViewModel.TYPE_PORTRAIT)
mUploadAvatar = true
}
}
ChooseDefaultAvatarDialog.REQUEST_CODE_DEFAULT_AVATAR -> {
val tag = requireArguments().getString(EntranceConsts.KEY_PARENT_TAG)
requireActivity().supportFragmentManager.findFragmentByTag(tag)
@ -145,7 +136,7 @@ class ChangeAvatarDialog : BaseDialogFragment() {
}
}
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "头像选择")
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "头像选择", IMAGE_TYPE_AVATAR)
startActivityForResult(intent, REQUEST_MEDIA_ICON)
}
@ -154,7 +145,6 @@ class ChangeAvatarDialog : BaseDialogFragment() {
}
companion object {
const val REQUEST_CROP_ICON = 12
const val REQUEST_MEDIA_ICON = 13
const val REQUEST_CODE_UPLOAD_AVATAR = 100