From c29efbefd7a557aff194bcbee57cff82e38da652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=99=A8?= Date: Wed, 6 Nov 2024 15:09:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=AA=92=E4=BD=93=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=8E=A7=E4=BB=B6=E4=BC=98=E5=8C=96=20(?= =?UTF-8?q?=E4=BA=8C=E3=80=81=E4=B8=89)=E2=80=94=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=20https://jira.shanqu.cc/browse/GHZSCY-6645?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 8 +- .../com/gh/base/BaseRichEditorActivity.kt | 22 +- .../common/provider/CropImageProviderImpl.kt | 58 ++++ .../com/gh/gamecenter/CropImageActivity.java | 68 ++--- .../ChooseGameCollectionCoverTypeDialog.kt | 7 +- .../publish/GameCollectionEditActivity.kt | 58 ++-- .../background/BackgroundClipActivity.kt | 15 +- .../PersonalityBackgroundFragment.kt | 23 +- .../qa/article/edit/ArticleEditActivity.kt | 12 +- .../qa/questions/edit/QuestionEditActivity.kt | 20 +- .../qa/video/publish/VideoPublishActivity.kt | 2 + .../qa/video/publish/VideoPublishFragment.kt | 34 ++- .../video/poster/PosterEditActivity.kt | 233 ++------------ .../video/poster/PreviewMaskView.kt | 91 ++++++ .../video/poster/video/VideoPosterFragment.kt | 287 +++++++++++++++--- .../poster/video/VideoPosterReviewAdapter.kt | 118 ++++--- .../poster/video/VideoPosterViewModel.kt | 91 +++++- .../video/upload/view/UploadVideoActivity.kt | 66 ++-- .../user/UserPortraitCropImageActivity.java | 185 ++++++----- .../user/avatar/ChangeAvatarDialog.kt | 16 +- .../res/drawable/bg_image_clip_submit.xml | 5 + .../drawable/ic_video_cover_edit_remove.xml | 23 ++ .../ic_video_cover_edit_take_photo.xml | 19 ++ .../shape_video_cover_edit_submit.xml | 5 + .../drawable/video_poster_preview_border.xml | 11 - .../res/layout/activity_background_clip.xml | 31 +- .../main/res/layout/activity_cropimage.xml | 48 +-- .../res/layout/actvitity_video_cover_edit.xml | 11 + .../res/layout/fragment_video_cover_edit.xml | 133 ++++++++ .../res/layout/video_poster_preview_item.xml | 67 ++-- app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../feature/selector/LocalMediaActivity.kt | 9 +- .../feature/selector/LocalMediaAdapter.kt | 2 +- .../feature/selector/LocalMediaFragment.kt | 66 +++- .../selector/LocalMediaPreviewFragment.kt | 40 ++- .../feature/selector/LocalMediaRepository.kt | 4 + .../feature/selector/LocalMediaViewModel.kt | 4 + .../main/res/layout/piece_media_control.xml | 6 +- .../gamecenter/common/constant/Constants.java | 2 + .../common/constant/EntranceConsts.java | 3 + .../gamecenter/common/constant/RouteConsts.kt | 1 + .../common/view/CropImageCustom.java | 36 ++- .../common/view/CropImageZoomView.java | 82 ++++- .../gh/gamecenter/common/view/DividerView.kt | 43 +++ .../common/view/cropbox/CropBoxAnimator.kt | 102 +++++++ .../common/view/cropbox/CropBoxDrawer.kt | 40 +++ .../common/view/cropbox/CropBoxStyle.kt | 35 +++ .../common/view/cropbox/CropBoxView.kt | 82 +++++ module_common/src/main/res/values/attrs.xml | 23 +- .../core/provider/ICropImageProvider.kt | 10 + 51 files changed, 1669 insertions(+), 690 deletions(-) create mode 100644 app/src/main/java/com/gh/common/provider/CropImageProviderImpl.kt create mode 100644 app/src/main/java/com/gh/gamecenter/video/poster/PreviewMaskView.kt create mode 100644 app/src/main/res/drawable/bg_image_clip_submit.xml create mode 100644 app/src/main/res/drawable/ic_video_cover_edit_remove.xml create mode 100644 app/src/main/res/drawable/ic_video_cover_edit_take_photo.xml create mode 100644 app/src/main/res/drawable/shape_video_cover_edit_submit.xml delete mode 100644 app/src/main/res/drawable/video_poster_preview_border.xml create mode 100644 app/src/main/res/layout/actvitity_video_cover_edit.xml create mode 100644 app/src/main/res/layout/fragment_video_cover_edit.xml create mode 100644 module_common/src/main/java/com/gh/gamecenter/common/view/DividerView.kt create mode 100644 module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxAnimator.kt create mode 100644 module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxDrawer.kt create mode 100644 module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxStyle.kt create mode 100644 module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxView.kt create mode 100644 module_core/src/main/java/com/gh/gamecenter/core/provider/ICropImageProvider.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 426173fc14..ba19de7c31 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -517,10 +517,6 @@ android:name=".video.data.VideoDataActivity" android:screenOrientation="portrait" /> - - @@ -809,6 +805,10 @@ android:screenOrientation="portrait" android:theme="@style/AppCompatTheme.APP" /> + + diff --git a/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt b/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt index 085be61fd9..0fa0dd73ed 100644 --- a/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt +++ b/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt @@ -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 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 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 diff --git a/app/src/main/java/com/gh/common/provider/CropImageProviderImpl.kt b/app/src/main/java/com/gh/common/provider/CropImageProviderImpl.kt new file mode 100644 index 0000000000..a830b39762 --- /dev/null +++ b/app/src/main/java/com/gh/common/provider/CropImageProviderImpl.kt @@ -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, 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/CropImageActivity.java b/app/src/main/java/com/gh/gamecenter/CropImageActivity.java index 71ea06d98c..07d597f7ec 100644 --- a/app/src/main/java/com/gh/gamecenter/CropImageActivity.java +++ b/app/src/main/java/com/gh/gamecenter/CropImageActivity.java @@ -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 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); } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/ChooseGameCollectionCoverTypeDialog.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/ChooseGameCollectionCoverTypeDialog.kt index 4d24af7158..25d60f3b9e 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/ChooseGameCollectionCoverTypeDialog.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/ChooseGameCollectionCoverTypeDialog.kt @@ -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())) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index 87db37df3b..6f4b6c67d7 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -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(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(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 diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundClipActivity.kt b/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundClipActivity.kt index 5310777af7..1cd4ea71c8 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundClipActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundClipActivity.kt @@ -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 } } diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/background/PersonalityBackgroundFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/background/PersonalityBackgroundFragment.kt index f16ea7d18d..f4a4bb2942 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/background/PersonalityBackgroundFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/background/PersonalityBackgroundFragment.kt @@ -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 } 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 f97ec4e9d4..f96ee40f71 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 @@ -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(), 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(), 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) { 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 dc0e58b6a9..cd98e4c0b1 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 @@ -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(), @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(), 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(), finish() } } + SaveDraftType.AUTO -> { if (pair.second) { if (mPostDraftsCount >= SAVE_DRAFTS_TOAST_COUNT) { @@ -478,6 +483,7 @@ class QuestionEditActivity : BaseRichEditorActivity(), } } } + SaveDraftType.SKIP -> { if (pair.second) { Utils.toast(this, "问题已保存到草稿箱") @@ -690,7 +696,12 @@ class QuestionEditActivity : BaseRichEditorActivity(), 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(), 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) { 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 index 16184ad840..4ae31e3334 100644 --- 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 @@ -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(R.id.backBtn).setOnClickListener { onBackPressed() } 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 index c169ee71ed..1380870b39 100644 --- 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 @@ -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( diff --git a/app/src/main/java/com/gh/gamecenter/video/poster/PosterEditActivity.kt b/app/src/main/java/com/gh/gamecenter/video/poster/PosterEditActivity.kt index 3935023cb9..16c5797374 100644 --- a/app/src/main/java/com/gh/gamecenter/video/poster/PosterEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/poster/PosterEditActivity.kt @@ -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(R.id.tab_title) - .setTextColor(resources.getColorStateList(R.color.poster_text_tabbar_style)) - return tabCustomView - } - - override fun initFragmentList(fragments: MutableList) { - val videoPath = intent.getStringExtra(EntranceConsts.KEY_PATH_VIDEO) - val videoEntity = intent.getParcelableExtra(VideoEntity::class.java.simpleName) - mPhotoPosterFragment = PhotoPosterFragment() - mVideoPosterFragment = VideoPosterFragment.newInstance(videoPath, videoEntity) - fragments.add(mVideoPosterFragment) - fragments.add(mPhotoPosterFragment) - } - - override fun initTabTitleList(tabTitleList: MutableList) { - 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/poster/PreviewMaskView.kt b/app/src/main/java/com/gh/gamecenter/video/poster/PreviewMaskView.kt new file mode 100644 index 0000000000..c7f1c48a09 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/video/poster/PreviewMaskView.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterFragment.kt b/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterFragment.kt index 4141953b8c..6d18a2024c 100644 --- a/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterFragment.kt @@ -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() { - 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 + + 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(EntranceConsts.KEY_VIDEO_ENTITY) super.onCreate(savedInstanceState) - mBinding = FragmentVideoPosterBinding.bind(mCachedView) - val videoPath = arguments?.getString(EntranceConsts.KEY_PATH_VIDEO) - val videoEntity = arguments?.getParcelable(VideoEntity::class.java.simpleName) + val videoCoverCache = + SPUtils.getString(Constants.SP_VIDEO_COVER_CACHE).toObject() + 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() { + 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() } + + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterReviewAdapter.kt b/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterReviewAdapter.kt index f5d667ae4d..1c7ea4f30f 100644 --- a/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterReviewAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/video/poster/video/VideoPosterReviewAdapter.kt @@ -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 = ArrayList() - private val mSelectMap = HashMap() - - init { - mSelectMap[0] = true // default select - } + private var selectedPosition = 0 fun submitList(listData: List) { 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) { + 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" } } 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 6924ed7034..d11fb3a48a 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 @@ -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>() val previewLiveData = MutableLiveData() + private val _selectedImage = MutableLiveData() + val selectedImage: LiveData = _selectedImage + fun setSelectedImage(videoPreview: VideoPreview?) { + _selectedImage.value = videoPreview + if (videoPreview != null) { + setPreviewImage(SELECTED_IMAGE_POSITION, videoPreview) + } + + } + + private val _previewPosition = MutableLiveData() + val previewPosition: LiveData = _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() + 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() @@ -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 create(modelClass: Class): T { - return VideoPosterViewModel(mApplication, mVideoPath, mVideoEntity) as T + return VideoPosterViewModel(mApplication, mVideoPath, mVideoEntity, cache) as T } } } \ No newline at end of file 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 7c00d63b02..2ebe40600e 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 @@ -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 diff --git a/app/src/main/java/com/halo/assistant/fragment/user/UserPortraitCropImageActivity.java b/app/src/main/java/com/halo/assistant/fragment/user/UserPortraitCropImageActivity.java index 7a94e457c8..8595ed93e8 100644 --- a/app/src/main/java/com/halo/assistant/fragment/user/UserPortraitCropImageActivity.java +++ b/app/src/main/java/com/halo/assistant/fragment/user/UserPortraitCropImageActivity.java @@ -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) 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() { - @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) 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() { + @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; } } diff --git a/app/src/main/java/com/halo/assistant/fragment/user/avatar/ChangeAvatarDialog.kt b/app/src/main/java/com/halo/assistant/fragment/user/avatar/ChangeAvatarDialog.kt index 497e4306c6..e416684d8b 100644 --- a/app/src/main/java/com/halo/assistant/fragment/user/avatar/ChangeAvatarDialog.kt +++ b/app/src/main/java/com/halo/assistant/fragment/user/avatar/ChangeAvatarDialog.kt @@ -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 diff --git a/app/src/main/res/drawable/bg_image_clip_submit.xml b/app/src/main/res/drawable/bg_image_clip_submit.xml new file mode 100644 index 0000000000..7a3c0d9f51 --- /dev/null +++ b/app/src/main/res/drawable/bg_image_clip_submit.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_video_cover_edit_remove.xml b/app/src/main/res/drawable/ic_video_cover_edit_remove.xml new file mode 100644 index 0000000000..9aa11694c5 --- /dev/null +++ b/app/src/main/res/drawable/ic_video_cover_edit_remove.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_video_cover_edit_take_photo.xml b/app/src/main/res/drawable/ic_video_cover_edit_take_photo.xml new file mode 100644 index 0000000000..c50c281799 --- /dev/null +++ b/app/src/main/res/drawable/ic_video_cover_edit_take_photo.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/shape_video_cover_edit_submit.xml b/app/src/main/res/drawable/shape_video_cover_edit_submit.xml new file mode 100644 index 0000000000..7a3c0d9f51 --- /dev/null +++ b/app/src/main/res/drawable/shape_video_cover_edit_submit.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/video_poster_preview_border.xml b/app/src/main/res/drawable/video_poster_preview_border.xml deleted file mode 100644 index 1419eeb800..0000000000 --- a/app/src/main/res/drawable/video_poster_preview_border.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_background_clip.xml b/app/src/main/res/layout/activity_background_clip.xml index 9a80012f5e..413c72ddff 100644 --- a/app/src/main/res/layout/activity_background_clip.xml +++ b/app/src/main/res/layout/activity_background_clip.xml @@ -8,48 +8,41 @@ + android:layout_height="match_parent" + android:background="#1A1A1A"> - - - - - - + android:background="@color/ui_background_fixed_dark" /> - + - - + + + \ No newline at end of file diff --git a/app/src/main/res/layout/actvitity_video_cover_edit.xml b/app/src/main/res/layout/actvitity_video_cover_edit.xml new file mode 100644 index 0000000000..29a8ecd6eb --- /dev/null +++ b/app/src/main/res/layout/actvitity_video_cover_edit.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_video_cover_edit.xml b/app/src/main/res/layout/fragment_video_cover_edit.xml new file mode 100644 index 0000000000..5e4204db1b --- /dev/null +++ b/app/src/main/res/layout/fragment_video_cover_edit.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/video_poster_preview_item.xml b/app/src/main/res/layout/video_poster_preview_item.xml index 4b3a243c4a..12959fda9e 100644 --- a/app/src/main/res/layout/video_poster_preview_item.xml +++ b/app/src/main/res/layout/video_poster_preview_item.xml @@ -2,38 +2,49 @@ + android:layout_height="56dp"> - - - - - - - + + + + + + + diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 86bfa927e1..78d1b045e8 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -593,6 +593,7 @@ 啟動遊戲 加載完成,馬上開始遊戲吧 %1$d款遊戲加載完成,馬上開始遊戲吧 + 從相簿選擇或選擇影片截圖 遊戲上線後,您將在訊息中心收到通知 遊戲上線後,您將在訊息中心收到通知,及以下方式提醒 簡訊提醒 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b6b1377527..b4b776f11f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -593,6 +593,7 @@ 启动游戏 加载完成,马上开始游戏吧 %1$d款游戏加载完成,马上开始游戏吧 + 从相册选择或选择视频截图 游戏上线后,您将在消息中心收到通知 游戏上线后,您将在消息中心收到通知,及以下方式提醒 短信提醒 diff --git a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaActivity.kt b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaActivity.kt index bc001ebc74..956933457c 100644 --- a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaActivity.kt +++ b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaActivity.kt @@ -41,13 +41,18 @@ class LocalMediaActivity : BaseActivity() { context: Context, chooseType: ChooseType, maxChooseCount: Int = 1, - entrance: String + entrance: String, + imageType: Int = 0 ): Intent { return Intent(context, LocalMediaActivity::class.java).apply { putExtra(EntranceConsts.KEY_TYPE, chooseType.value) putExtra(EntranceConsts.KEY_CHOOSE_MAX_COUNT, maxChooseCount) - putExtra(EntranceConsts.KEY_QUICK_CHOOSE, maxChooseCount == 1) + putExtra( + EntranceConsts.KEY_QUICK_CHOOSE, + if (chooseType == ChooseType.VIDEO) false else maxChooseCount == 1 + ) putExtra(EntranceConsts.KEY_ENTRANCE, entrance) + putExtra(EntranceConsts.KEY_IMAGE_TYPE, imageType) } } } diff --git a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaAdapter.kt b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaAdapter.kt index 840a3617bb..a78d2c8a23 100644 --- a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaAdapter.kt +++ b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaAdapter.kt @@ -93,7 +93,7 @@ class LocalMediaAdapter( holder.binding.checkImageView.visibility = View.GONE holder.itemView.setOnClickListener { - viewModel.addSelection(item) + viewModel.setSelection(item) viewModel.postSelectedResult() } } else { diff --git a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaFragment.kt b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaFragment.kt index 63b28ae2e1..a26b67c4ff 100644 --- a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaFragment.kt +++ b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaFragment.kt @@ -14,6 +14,7 @@ import android.widget.PopupWindow 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.constant.RouteConsts import com.gh.gamecenter.common.entity.LocalVideoEntity import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.toColor @@ -23,11 +24,13 @@ import com.gh.gamecenter.core.utils.MD5Utils import com.gh.gamecenter.common.utils.enlargeTouchArea import com.gh.gamecenter.common.utils.viewModelProviderFromParent import com.gh.gamecenter.common.view.GridSpacingItemDecoration +import com.gh.gamecenter.core.provider.ICropImageProvider import com.zhihu.matisse.Matisse import com.zhihu.matisse.MimeType import com.gh.gamecenter.livedata.EventObserver import com.gh.gamecenter.selector.databinding.FragmentLocalMediaBinding +import com.therouter.TheRouter import com.zhihu.matisse.internal.entity.Album import com.zhihu.matisse.internal.entity.Item @@ -60,6 +63,7 @@ class LocalMediaFragment : BaseFragment(), AlbumMediaCollection.AlbumMediaC private val albumCollection = AlbumCollection() private var isFirstAlbumLoad = true + private var imageType = 0 override fun getLayoutId(): Int = 0 @@ -77,12 +81,15 @@ class LocalMediaFragment : BaseFragment(), AlbumMediaCollection.AlbumMediaC isQuickChoose = savedInstanceState?.getBoolean(EntranceConsts.KEY_QUICK_CHOOSE, false) ?: arguments?.getBoolean(EntranceConsts.KEY_QUICK_CHOOSE, false) ?: false + imageType = savedInstanceState?.getInt(EntranceConsts.KEY_IMAGE_TYPE, 0) + ?: arguments?.getInt(EntranceConsts.KEY_IMAGE_TYPE, 0) ?: 0 viewModel = viewModelProviderFromParent(LocalMediaViewModel.Factory(maxChooseCount)) // 页面恢复时,恢复选中的数据 if (savedInstanceState != null) { - val savedSelectedItem: ArrayList? = savedInstanceState.getParcelableArrayList(EntranceConsts.KEY_VIDEO_LIST) + val savedSelectedItem: ArrayList? = + savedInstanceState.getParcelableArrayList(EntranceConsts.KEY_VIDEO_LIST) if (savedSelectedItem != null) { for (selectedItem in savedSelectedItem) { viewModel.addSelection(selectedItem) @@ -164,14 +171,27 @@ class LocalMediaFragment : BaseFragment(), AlbumMediaCollection.AlbumMediaC ) } intent.putExtra(LocalVideoEntity::class.java.name, localVideoList) + + requireActivity().setResult(Activity.RESULT_OK, intent) + requireActivity().finish() } else { val data = viewModel.selectedItemList.map { it.contentUri }.toList() val path = data.map { PathUtils.getPath(requireContext(), it) }.toList() - intent.putParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION, ArrayList(data)) - intent.putStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH, ArrayList(path)) + + val imageProvider = TheRouter.get(ICropImageProvider::class.java) + val toIntent = imageProvider?.getCropImageIntent(data, imageType, mEntrance, requireContext()) + if (toIntent != null) { + // 需要裁剪 + startActivityForResult(toIntent, REQUEST_CROP_ICON) + } else { + intent.putParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION, ArrayList(data)) + intent.putStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH, ArrayList(path)) + requireActivity().setResult(Activity.RESULT_OK, intent) + requireActivity().finish() + } } - requireActivity().setResult(Activity.RESULT_OK, intent) - requireActivity().finish() + + } } @@ -196,12 +216,21 @@ class LocalMediaFragment : BaseFragment(), AlbumMediaCollection.AlbumMediaC binding.pieceMediaControl.previewTv.isEnabled = isNotEmpty binding.pieceMediaControl.confirmTv.isEnabled = isNotEmpty if (isNotEmpty) { - binding.pieceMediaControl.previewTv.setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(requireContext())) + binding.pieceMediaControl.previewTv.setTextColor( + com.gh.gamecenter.common.R.color.text_aw_primary.toColor( + requireContext() + ) + ) binding.pieceMediaControl.previewTv.alpha = 1F binding.pieceMediaControl.confirmTv.alpha = 1F - binding.pieceMediaControl.confirmTv.text = "${getString(com.gh.gamecenter.common.R.string.confirm)}(${it})" + binding.pieceMediaControl.confirmTv.text = + "${getString(com.gh.gamecenter.common.R.string.confirm)}(${it})" } else { - binding.pieceMediaControl.previewTv.setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(requireContext())) + binding.pieceMediaControl.previewTv.setTextColor( + com.gh.gamecenter.common.R.color.text_aw_primary.toColor( + requireContext() + ) + ) binding.pieceMediaControl.previewTv.alpha = 0.6F binding.pieceMediaControl.confirmTv.alpha = 0.6F binding.pieceMediaControl.confirmTv.text = getString(com.gh.gamecenter.common.R.string.confirm) @@ -333,4 +362,25 @@ class LocalMediaFragment : BaseFragment(), AlbumMediaCollection.AlbumMediaC albumMediaCollection?.onDestroy() } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == Activity.RESULT_OK && data != null) { + when (requestCode) { + REQUEST_CROP_ICON -> { + // 裁剪成功 + if (data != null) { + val intent = Intent() + intent.putExtras(data) + requireActivity().setResult(Activity.RESULT_OK, intent) + requireActivity().finish() + } + } + } + } + } + + companion object { + private const val REQUEST_CROP_ICON = 13 + } + } \ No newline at end of file diff --git a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaPreviewFragment.kt b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaPreviewFragment.kt index 36f720eba1..764afda5c3 100644 --- a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaPreviewFragment.kt +++ b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaPreviewFragment.kt @@ -61,6 +61,7 @@ import com.github.piasy.biv.view.BigImageView import com.github.piasy.biv.view.FrescoImageViewFactory import com.lightgame.listeners.OnBackPressedListener import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView.CURRENT_STATE_PLAYING import com.zhihu.matisse.internal.entity.Album import com.zhihu.matisse.internal.entity.Item import java.io.File @@ -171,7 +172,12 @@ class LocalMediaPreviewFragment : BaseFragment(), OnPageChangeListener, OnB parentViewModel?.removeSelection(currentDisplayingItem!!) } else { if (parentViewModel?.addSelection(currentDisplayingItem!!) == false) { - ToastUtils.showToast("至多选择${parentViewModel?.maxSelectionSize}张图片") + val toastText = if (currentDisplayingItem?.isVideo == true) { + ToastUtils.showToast("至多选择${parentViewModel?.maxSelectionSize}条视频") + } else { + ToastUtils.showToast("至多选择${parentViewModel?.maxSelectionSize}张图片") + } + } } @@ -211,7 +217,11 @@ class LocalMediaPreviewFragment : BaseFragment(), OnPageChangeListener, OnB binding.mediaControl.confirmTv.text = "确定(${list.size})" binding.mediaControl.confirmTv.alpha = 1f } else { - binding.mediaControl.previewTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext())) + binding.mediaControl.previewTv.setTextColor( + com.gh.gamecenter.common.R.color.text_secondary.toColor( + requireContext() + ) + ) binding.mediaControl.confirmTv.text = "确定" binding.mediaControl.confirmTv.alpha = 0.6f } @@ -225,7 +235,8 @@ class LocalMediaPreviewFragment : BaseFragment(), OnPageChangeListener, OnB } if (previewItem?.isVideo != true - && currentDisplayingItem?.id == previewItem?.id ) { + && currentDisplayingItem?.id == previewItem?.id + ) { initEnterAnimation() } @@ -808,7 +819,9 @@ class LocalMediaPreviewFragment : BaseFragment(), OnPageChangeListener, OnB videoViewMap.put(position, binding.videoView) binding.videoView.updateThumb(item.contentUri.toString()) - binding.videoView.setAlOnClickListener { toggleToolbarAndBottomContainer() } + binding.videoView.setAlOnClickListener { + binding.videoView.startButton.performClick() + } setDragListener(binding.videoView) GSYVideoOptionBuilder() .setIsTouchWiget(false) @@ -819,7 +832,12 @@ class LocalMediaPreviewFragment : BaseFragment(), OnPageChangeListener, OnB .setReleaseWhenLossAudio(true) .setLooping(false) .setShowFullAnimation(false) + .setGSYStateUiListener { + toggleToolbarAndBottomContainer(it != CURRENT_STATE_PLAYING) + } .build(binding.videoView) + + } else { if (useEnterAndExitAnimation) { containerMap.put(position, view) @@ -829,7 +847,9 @@ class LocalMediaPreviewFragment : BaseFragment(), OnPageChangeListener, OnB } setDragListener(binding.iv) - binding.iv.setOnClickListener { toggleToolbarAndBottomContainer() } + binding.iv.setOnClickListener { + toggleToolbarAndBottomContainer(bottomContainer.visibility != View.VISIBLE) + } binding.iv.setImageLoaderCallback(object : SimpleImageLoader() { override fun onSuccess(image: File) { val ssiv = binding.iv.ssiv @@ -868,13 +888,13 @@ class LocalMediaPreviewFragment : BaseFragment(), OnPageChangeListener, OnB private fun isFadeOnly() = currentDisplayingItem?.isVideo == true || originLeftMap?.get(currentDisplayingItem?.uri?.toString()) == null - private fun toggleToolbarAndBottomContainer() { - if (bottomContainer.visibility == View.VISIBLE) { - bottomContainer.visibility = View.GONE - toolbarContainer.visibility = View.GONE - } else { + private fun toggleToolbarAndBottomContainer(showController: Boolean) { + if (showController) { bottomContainer.visibility = View.VISIBLE toolbarContainer.visibility = View.VISIBLE + } else { + bottomContainer.visibility = View.GONE + toolbarContainer.visibility = View.GONE } } diff --git a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaRepository.kt b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaRepository.kt index 053f11fbfc..fd6c6b66bb 100644 --- a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaRepository.kt +++ b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaRepository.kt @@ -48,6 +48,10 @@ class LocalMediaRepository { _selectedItemListFlow.tryEmit(newList) } + fun setSelection(item: Item) { + _selectedItemListFlow.tryEmit(arrayListOf(item)) + } + fun closeCursor() { albumCursor?.close() } diff --git a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaViewModel.kt b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaViewModel.kt index 4b0cc3661b..d66ea4c5e7 100644 --- a/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaViewModel.kt +++ b/feature/media_select/src/main/java/com/gh/gamecenter/feature/selector/LocalMediaViewModel.kt @@ -70,6 +70,10 @@ class LocalMediaViewModel(val maxSelectionSize: Int) : ViewModel() { return true } + fun setSelection(item: Item) { + localMediaRepo.setSelection(item) + } + fun removeSelection(item: Item) { localMediaRepo.removeSelection(item) } diff --git a/feature/media_select/src/main/res/layout/piece_media_control.xml b/feature/media_select/src/main/res/layout/piece_media_control.xml index 1623b5b49f..9e25019f73 100644 --- a/feature/media_select/src/main/res/layout/piece_media_control.xml +++ b/feature/media_select/src/main/res/layout/piece_media_control.xml @@ -2,9 +2,7 @@ + android:background="@color/ui_surface_fixed_dark"> @@ -24,6 +23,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:alpha="0.6" + android:layout_marginHorizontal="16dp" android:background="@drawable/button_blue_oval" android:enabled="false" android:gravity="center" diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java index 71c12d0658..44f0e97a90 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java @@ -475,4 +475,6 @@ public class Constants { public static final String TOOL_MAP_PACKAGE_NAME = "com.gh.toolmap";// 光环工具服务APP包名 + public static final String SP_VIDEO_COVER_CACHE = "sp_video_cover_cache"; + } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java index 574f8d4cae..df7b551be9 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java @@ -209,6 +209,7 @@ public class EntranceConsts { public static final String KEY_OPEN_KEYBOARD = "openKeyboard"; public static final String KEY_PATH_VIDEO = "pathVideo"; public static final String KEY_VIDEO_ID = "videoId"; + public static final String KEY_VIDEO_ENTITY = "key_video_entity"; public static final String KEY_DIRECT_COMMENT = "directComment"; public static final String KEY_SORT = "sort"; public static final String KEY_AMWAY = "amway"; @@ -219,6 +220,8 @@ public class EntranceConsts { public static final String KEY_COLLECTION_NAME = "collectionName"; public static final String KEY_NAVIGATION_TITLE = "navigationTitle"; + public static final String KEY_IMAGE_CROP_STYLE = "imageCropStyle"; + public static final String KEY_IMAGE_TYPE = "imageType"; public static final String KEY_IMAGE_CROP_RATIO = "imageCropRatio"; public static final String KEY_OPEN_VIDEO_STREAMING = "open_video_streaming"; public static final String KEY_REFERER = "referer"; diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt b/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt index 49f714c023..e37aa59fd2 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt @@ -14,6 +14,7 @@ object RouteConsts { const val forumVideoDetailActivity = "/app/forumVideoDetailActivity" const val libaoDetailActivity = "/app/libaoDetailActivity" const val fullScreenVideoActivity = "/app/FullScreenVideoActivity" + const val videoCoverEditActivity = "/app/videoCoverEditActivity" const val gameDetailActivity = "/activity/game_detail" const val userHomeActivity = "/activity/user_home" diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageCustom.java b/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageCustom.java index 379450bedc..6f1d6a0084 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageCustom.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageCustom.java @@ -10,6 +10,8 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import com.gh.gamecenter.common.R; +import com.gh.gamecenter.common.view.cropbox.CropBoxStyle; +import com.gh.gamecenter.common.view.cropbox.CropBoxView; import com.gh.gamecenter.core.utils.DisplayUtils; import java.io.BufferedOutputStream; @@ -19,9 +21,9 @@ import java.io.FileOutputStream; public class CropImageCustom extends RelativeLayout { private CropImageZoomView mZoomImageView; - private CropImageBorderView mClipImageView; + private CropBoxView mClipImageView; - private int mHorizontalPadding = 20; + private int mHorizontalPadding = 16; private float mRatio = 1F; // 裁剪比例 @@ -29,7 +31,7 @@ public class CropImageCustom extends RelativeLayout { super(context, attrs); mZoomImageView = new CropImageZoomView(context); - mClipImageView = new CropImageBorderView(context); + mClipImageView = new CropBoxView(context); android.view.ViewGroup.LayoutParams lp = new LayoutParams( android.view.ViewGroup.LayoutParams.MATCH_PARENT, @@ -47,13 +49,21 @@ public class CropImageCustom extends RelativeLayout { ta.recycle(); mZoomImageView.setHorizontalPadding(mHorizontalPadding, mRatio); - mClipImageView.setHorizontalPadding(mHorizontalPadding, mRatio); + mClipImageView.setHorizontalPaddingBoxStyle(mHorizontalPadding, new CropBoxStyle.Rectangle(mRatio)); + mZoomImageView.setOnDragStateChangeListener(isDragging -> { + mClipImageView.changeDragState(isDragging); + }); } - public void setCropRatio(float ratio) { - this.mRatio = ratio; + public void setCropBoxStyle(CropBoxStyle boxStyle) { + if (boxStyle instanceof CropBoxStyle.Circle) { + mRatio = 1F; + } + if (boxStyle instanceof CropBoxStyle.Rectangle) { + mRatio = ((CropBoxStyle.Rectangle) boxStyle).getRadio(); + } mZoomImageView.setHorizontalPadding(mHorizontalPadding, mRatio); - mClipImageView.setHorizontalPadding(mHorizontalPadding, mRatio); + mClipImageView.setHorizontalPaddingBoxStyle(mHorizontalPadding, boxStyle); } public void addAssistView(View view) { @@ -99,4 +109,16 @@ public class CropImageCustom extends RelativeLayout { mZoomImageView.setImageBitmap(bitmap); } + public void reset() { + mZoomImageView.reset(); + } + + public CropImageZoomView.TransformationValues getTransformationValues() { + return mZoomImageView.getTransformationValues(); + } + + public void setTransformationValues(CropImageZoomView.TransformationValues values) { + mZoomImageView.setTransformationValues(values); + } + } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageZoomView.java b/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageZoomView.java index ad0e7c5115..aa43e66704 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageZoomView.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/CropImageZoomView.java @@ -14,6 +14,7 @@ import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.View; import android.view.View.OnTouchListener; +import android.view.ViewConfiguration; import android.view.ViewTreeObserver; import androidx.appcompat.widget.AppCompatImageView; @@ -30,11 +31,13 @@ public class CropImageZoomView extends AppCompatImageView implements */ private final float[] matrixValues = new float[9]; private final Matrix mScaleMatrix = new Matrix(); + private TransformationValues cacheValues; /** * 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0 */ private float initScale = 1.0f; private boolean once = true; + /** * 缩放的手势检测 */ @@ -65,13 +68,17 @@ public class CropImageZoomView extends AppCompatImageView implements private int mWidth; + private boolean isDragging = false; + + private int touchSlop; + public CropImageZoomView(Context context) { this(context, null); } public CropImageZoomView(Context context, AttributeSet attrs) { super(context, attrs); - + touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); setScaleType(ScaleType.MATRIX); mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() { @@ -109,6 +116,20 @@ public class CropImageZoomView extends AppCompatImageView implements return matrixValues[Matrix.MSCALE_X]; } + public TransformationValues getTransformationValues() { + mScaleMatrix.getValues(matrixValues); + return new TransformationValues( + matrixValues[Matrix.MSCALE_X], + matrixValues[Matrix.MSCALE_Y], + matrixValues[Matrix.MTRANS_X], + matrixValues[Matrix.MTRANS_Y] + ); + } + + public void setTransformationValues(TransformationValues values) { + cacheValues = values; + } + @Override public boolean onScale(ScaleGestureDetector detector) { float scale = getScale(); @@ -252,6 +273,18 @@ public class CropImageZoomView extends AppCompatImageView implements checkBorder(); setImageMatrix(mScaleMatrix); } + + boolean isMoving = false; + if (Math.abs(dx) > mTouchSlop) { + isMoving = true; + } + if (Math.abs(dy) > mTouchSlop) { + isMoving = true; + } + if (isMoving && stateListener != null && !isDragging) { + isDragging = true; + stateListener.onStateChanged(true); + } } mLastX = x; mLastY = y; @@ -260,6 +293,10 @@ public class CropImageZoomView extends AppCompatImageView implements case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: lastPointerCount = 0; + if (stateListener != null && isDragging) { + isDragging = false; + stateListener.onStateChanged(false); + } break; } @@ -333,9 +370,17 @@ public class CropImageZoomView extends AppCompatImageView implements initScale = scale; SCALE_MID = initScale * 2; SCALE_MAX = initScale * 4; - mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);// 平移至屏幕中心 - mScaleMatrix.postScale(scale, scale, getWidth() / 2, - getHeight() / 2);// 设置缩放比例 + + // 如果有缓存,直接显示缓存 + if (cacheValues != null) { + mScaleMatrix.postScale(cacheValues.scaleX, cacheValues.scaleY); + mScaleMatrix.postTranslate(cacheValues.translateX, cacheValues.translateY); + cacheValues = null; + } else { + mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);// 平移至屏幕中心 + mScaleMatrix.postScale(scale, scale, getWidth() / 2, + getHeight() / 2);// 设置缩放比例 + } // 图片移动至屏幕中心 setImageMatrix(mScaleMatrix); once = false; @@ -365,6 +410,11 @@ public class CropImageZoomView extends AppCompatImageView implements this.mRatio = ratio; } + public void reset() { + once = true; + mScaleMatrix.reset(); + } + /** * 自动缩放的任务 * @@ -424,4 +474,28 @@ public class CropImageZoomView extends AppCompatImageView implements } } + private OnDragStateListener stateListener; + + interface OnDragStateListener { + + void onStateChanged(boolean isDragging); + } + + void setOnDragStateChangeListener(OnDragStateListener listener) { + stateListener = listener; + } + + public static class TransformationValues { + public final float scaleX; + public final float scaleY; + public final float translateX; + public final float translateY; + + public TransformationValues(float scaleX, float scaleY, float translateX, float translateY) { + this.scaleX = scaleX; + this.scaleY = scaleY; + this.translateX = translateX; + this.translateY = translateY; + } + } } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/DividerView.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/DividerView.kt new file mode 100644 index 0000000000..e6fafbf224 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/DividerView.kt @@ -0,0 +1,43 @@ +package com.gh.gamecenter.common.view + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import com.gh.gamecenter.common.R +import com.gh.gamecenter.common.utils.dip2px + +class DividerView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : View(context, attrs, defStyleAttr, defStyleRes) { + + private var size = DIVIDER_SIZE_THIN + + init { + initAttrs(context, attrs) + } + + private fun initAttrs(context: Context, attrs: AttributeSet?) { + attrs ?: return + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.DividerView) + size = typedArray.getInt(R.styleable.DividerView_divider_size, DIVIDER_SIZE_REGULAR) + typedArray.recycle() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val height = when (size) { + DIVIDER_SIZE_THIN -> 1 + DIVIDER_SIZE_MEDIUM -> 0.5F.dip2px() + else -> 1F.dip2px() + } + setMeasuredDimension(widthMeasureSpec, height) + } + + companion object { + private const val DIVIDER_SIZE_THIN = 0 + private const val DIVIDER_SIZE_MEDIUM = 1 + private const val DIVIDER_SIZE_REGULAR = 12 + } +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxAnimator.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxAnimator.kt new file mode 100644 index 0000000000..0827db308a --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxAnimator.kt @@ -0,0 +1,102 @@ +package com.gh.gamecenter.common.view.cropbox + +import android.animation.ValueAnimator +import android.graphics.Color +import android.os.Handler +import android.os.Looper +import android.view.animation.AccelerateDecelerateInterpolator + +class CropBoxAnimator(private val block: (Int) -> Unit) { + + private var currentState: AnimatorState = AnimatorState.Default + + private val handler = Handler(Looper.getMainLooper()) + + + private val valueAnimator by lazy { + val startColor = Color.parseColor("#CC000000") + val endColor = Color.parseColor("#33000000") + ValueAnimator.ofArgb(startColor, endColor) + .setDuration(ANIMATOR_DURATION) + .apply { + interpolator = AccelerateDecelerateInterpolator() + addUpdateListener { animator -> + val animatedColor = animator.animatedValue as Int + block(animatedColor) + val time = animator.currentPlayTime + if (time >= ANIMATOR_DURATION) { + if (currentState is AnimatorState.Playing) { + currentState = AnimatorState.Dragging + } + if (currentState is AnimatorState.Reversing) { + currentState = AnimatorState.Default + } + } + } + + } + } + + private val task = Runnable { + // 延时结束,执行反向渐变动画 + currentState = AnimatorState.Reversing + valueAnimator.reverse() + } + + + fun changeDragState(isDragging: Boolean) { + when (currentState) { + is AnimatorState.Default -> { + // 执行正向渐变动画 + if (isDragging) { + currentState = AnimatorState.Playing + valueAnimator.start() + } + } + + is AnimatorState.Playing -> { + if (!isDragging) { + currentState = AnimatorState.Delay + handler.removeCallbacksAndMessages(null) + handler.postDelayed(task, DELAY_DURATION) + } + } + + is AnimatorState.Dragging -> { + if (!isDragging) { + currentState = AnimatorState.Delay + handler.removeCallbacksAndMessages(null) + handler.postDelayed(task, DELAY_DURATION) + } + } + + is AnimatorState.Delay -> { + if (isDragging) { + currentState = AnimatorState.Dragging + handler.removeCallbacksAndMessages(null) + } + } + + is AnimatorState.Reversing -> { + if (isDragging) { + currentState = AnimatorState.Playing + valueAnimator.reverse() + } + } + + } + } + + companion object { + private const val ANIMATOR_DURATION = 200L + private const val DELAY_DURATION = 500L + } + + sealed class AnimatorState { + object Default : AnimatorState() + object Playing : AnimatorState() + object Dragging : AnimatorState() + object Delay : AnimatorState() + object Reversing : AnimatorState() + } +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxDrawer.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxDrawer.kt new file mode 100644 index 0000000000..baf91298e6 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxDrawer.kt @@ -0,0 +1,40 @@ +package com.gh.gamecenter.common.view.cropbox + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF + +interface CropBoxDrawer { + + fun onDraw(rectF: RectF, canvas: Canvas, paint: Paint) + + fun onDrawDecoration(rectF: RectF, canvas: Canvas, paint: Paint) + + class CircleDrawer : CropBoxDrawer { + + override fun onDraw(rectF: RectF, canvas: Canvas, paint: Paint) { + canvas.drawCircle(rectF.centerX(), rectF.centerY(), rectF.width() / 2, paint) + } + + override fun onDrawDecoration(rectF: RectF, canvas: Canvas, paint: Paint) { + canvas.drawCircle(rectF.centerX(), rectF.centerY(), (rectF.width() - paint.strokeWidth) / 2, paint) + } + } + + class RectangleDrawer : CropBoxDrawer { + override fun onDraw(rectF: RectF, canvas: Canvas, paint: Paint) { + canvas.drawRect(rectF, paint) + } + + override fun onDrawDecoration(rectF: RectF, canvas: Canvas, paint: Paint) { + val realRectF = RectF( + rectF.left + paint.strokeWidth / 2, + rectF.top, + rectF.right - paint.strokeWidth / 2, + rectF.bottom + ) + canvas.drawRect(realRectF, paint) + } + + } +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxStyle.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxStyle.kt new file mode 100644 index 0000000000..1d5d6535f1 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxStyle.kt @@ -0,0 +1,35 @@ +package com.gh.gamecenter.common.view.cropbox + +import android.graphics.RectF +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +sealed class CropBoxStyle : Parcelable { + + fun getRect(width: Int, height: Int, horizontalPadding: Int): RectF { + // 计算矩形区域的宽度 + val boxWidth = width - 2 * horizontalPadding + val boxHeight = getBoxHeight(boxWidth) + val left = width - boxWidth - horizontalPadding + val top = (height - boxHeight) / 2 + val right = left + boxWidth + val bottom = top + boxHeight + return RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat()) + } + + abstract fun getBoxHeight(boxWidth: Int): Int + + object Circle : CropBoxStyle() { + + override fun getBoxHeight(boxWidth: Int): Int { + return boxWidth + } + } + + class Rectangle(val radio: Float) : CropBoxStyle() { + override fun getBoxHeight(boxWidth: Int): Int { + return (boxWidth * radio).toInt() + } + } +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxView.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxView.kt new file mode 100644 index 0000000000..7198ce95fc --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/cropbox/CropBoxView.kt @@ -0,0 +1,82 @@ +package com.gh.gamecenter.common.view.cropbox + +import android.content.Context +import android.graphics.* +import android.util.AttributeSet +import android.view.View +import com.gh.gamecenter.common.utils.dip2px + +class CropBoxView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : + View(context, attrs, defStyleAttr) { + + private var horizontalPadding = 0 + private var boxStyle: CropBoxStyle = CropBoxStyle.Rectangle(1F) + private var drawer: CropBoxDrawer = CropBoxDrawer.RectangleDrawer() + + private val paint by lazy { + Paint().apply { + isAntiAlias = true + isDither = true + color = Color.parseColor("#CC000000") + } + } + + private val borderPaint by lazy { + Paint().apply { + style = Paint.Style.STROKE + isAntiAlias = true + setColor(Color.WHITE) + strokeWidth = 0.5F.dip2px().toFloat() + } + } + + private val xFermode by lazy { + val mode = PorterDuff.Mode.CLEAR + PorterDuffXfermode(mode) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + val rect = boxStyle.getRect(width, height, horizontalPadding) + val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), paint, Canvas.ALL_SAVE_FLAG) + + canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint) + paint.xfermode = xFermode + drawer.onDraw(rect, canvas, paint) + paint.xfermode = null + + canvas.restoreToCount(layerId) + + // 绘制裁剪框边框 + drawer.onDrawDecoration(rect, canvas, borderPaint) + } + + fun setHorizontalPaddingBoxStyle(horizontalPadding: Int, boxStyle: CropBoxStyle) { + this.horizontalPadding = horizontalPadding + this.boxStyle = boxStyle + drawer = createDrawer(boxStyle) + } + + private val cropBoxAnimator by lazy { + CropBoxAnimator { + paint.color = it + postInvalidateOnAnimation() + } + } + + + fun changeDragState(isDragging: Boolean) { + cropBoxAnimator.changeDragState(isDragging) + } + + private fun createDrawer(boxStyle: CropBoxStyle) = when (boxStyle) { + is CropBoxStyle.Circle -> CropBoxDrawer.CircleDrawer() + is CropBoxStyle.Rectangle -> CropBoxDrawer.RectangleDrawer() + } + +} \ No newline at end of file diff --git a/module_common/src/main/res/values/attrs.xml b/module_common/src/main/res/values/attrs.xml index 71aaafdeba..47c952f916 100644 --- a/module_common/src/main/res/values/attrs.xml +++ b/module_common/src/main/res/values/attrs.xml @@ -249,15 +249,22 @@ - - - - + + + + - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/ICropImageProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/ICropImageProvider.kt new file mode 100644 index 0000000000..c148fde9e3 --- /dev/null +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/ICropImageProvider.kt @@ -0,0 +1,10 @@ +package com.gh.gamecenter.core.provider + +import android.content.Context +import android.content.Intent +import android.net.Uri + +interface ICropImageProvider { + + fun getCropImageIntent(data: List, imageType: Int, entrance: String, context: Context): Intent? +} \ No newline at end of file