Compare commits
13 Commits
refactor/s
...
feat/GHZSC
| Author | SHA1 | Date | |
|---|---|---|---|
| d146726b34 | |||
| 1a8efaa313 | |||
| 5e698fa497 | |||
| 7ba5f7239d | |||
| f7489be9a4 | |||
| 5c326b7150 | |||
| 6cfff8237a | |||
| c425198d47 | |||
| 6a5d081f43 | |||
| a9277f7925 | |||
| 4f59c10ce5 | |||
| 32658b1db0 | |||
| 6213ebdb88 |
@ -462,9 +462,6 @@
|
||||
android:name="com.gh.gamecenter.video.game.GameVideoActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.qa.editor.LocalMediaActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.servers.GameServersActivity"
|
||||
|
||||
@ -21,6 +21,8 @@ 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.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
|
||||
@ -30,6 +32,7 @@ import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.qa.editor.*
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.gh.gamecenter.qa.entity.EditorInsertEntity
|
||||
import com.gh.gamecenter.video.poster.PosterEditActivity
|
||||
import com.gh.gamecenter.video.upload.UploadManager
|
||||
@ -502,7 +505,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
this@BaseRichEditorActivity,
|
||||
LocalMediaActivity.ChooseType.VIDEO,
|
||||
ChooseType.VIDEO,
|
||||
maxChooseCount,
|
||||
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
|
||||
), INSERT_MEDIA_VIDEO_CODE
|
||||
@ -531,7 +534,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
|
||||
val maxChooseCount = if (imageCount + 10 <= MAX_IMAGE_COUNT) 10 else MAX_IMAGE_COUNT - imageCount
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
this@BaseRichEditorActivity,
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
ChooseType.IMAGE,
|
||||
maxChooseCount,
|
||||
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
|
||||
)
|
||||
|
||||
@ -20,7 +20,7 @@ import com.gh.gamecenter.core.runOnUiThread
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.entity.ForumDetailEntity
|
||||
import com.gh.gamecenter.entity.LocalVideoEntity
|
||||
import com.gh.gamecenter.common.entity.LocalVideoEntity
|
||||
import com.gh.gamecenter.entity.QuoteCountEntity
|
||||
import com.gh.gamecenter.qa.BbsType
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
@ -39,8 +39,6 @@ import retrofit2.HttpException
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.LinkedHashMap
|
||||
import kotlin.collections.set
|
||||
|
||||
|
||||
@ -2,23 +2,19 @@ package com.gh.gamecenter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
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 com.gh.gamecenter.common.base.activity.ToolBarActivity;
|
||||
import com.gh.gamecenter.common.utils.BitmapUtils;
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity;
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils;
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts;
|
||||
import com.gh.gamecenter.common.view.CropImageCustom;
|
||||
import com.gh.gamecenter.core.utils.SimpleImageLoader;
|
||||
import com.github.piasy.biv.view.BigImageView;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
@ -26,14 +22,10 @@ import androidx.annotation.NonNull;
|
||||
/**
|
||||
* 裁剪图片
|
||||
*/
|
||||
public class CropImageActivity extends ToolBarActivity {
|
||||
|
||||
protected CropImageCustom mCropImageCustom;
|
||||
public class CropImageActivity extends BaseActivity {
|
||||
|
||||
public static final String RESULT_CLIP_PATH = "result_clip_path";
|
||||
|
||||
private SoftReference<Bitmap> reference;
|
||||
|
||||
protected boolean mBlackTheme = false;
|
||||
|
||||
@NonNull
|
||||
@ -67,74 +59,50 @@ public class CropImageActivity extends ToolBarActivity {
|
||||
mBlackTheme = getIntent().getBooleanExtra(EntranceConsts.KEY_BLACK_THEME, false);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mCropImageCustom = findViewById(R.id.cropimage_custom);
|
||||
View statusBarView = findViewById(R.id.status_bar);
|
||||
|
||||
mTitleTv.setTextColor(mBlackTheme ? Color.WHITE : Color.BLACK);
|
||||
mToolbar.setBackgroundColor(getResources().getColor(mBlackTheme ? R.color.text_28282E : R.color.white));
|
||||
statusBarView.setBackgroundColor(getResources().getColor(mBlackTheme ? R.color.text_28282E : 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(R.color.text_theme));
|
||||
BigImageView iv = findViewById(R.id.iv);
|
||||
iv.showImage(Uri.parse(getIntent().getStringExtra(EntranceConsts.KEY_PATH)));
|
||||
iv.setInitScaleType(BigImageView.INIT_SCALE_TYPE_FIT_XY);
|
||||
iv.setImageLoaderCallback(new SimpleImageLoader() {
|
||||
@Override
|
||||
public void onSuccess(File image) {
|
||||
super.onSuccess(image);
|
||||
iv.getSSIV().setMinimumDpi(80);
|
||||
}
|
||||
});
|
||||
|
||||
float ratio = getIntent().getFloatExtra(EntranceConsts.KEY_IMAGE_CROP_RATIO, 1F);
|
||||
mCropImageCustom.setCropRatio(ratio);
|
||||
|
||||
int assistRes = getIntent().getIntExtra(EntranceConsts.KEY_ASSIST_RES, -1);
|
||||
if (assistRes > 0) {
|
||||
View view = LayoutInflater.from(this).inflate(assistRes, null, false);
|
||||
addAssistView(view);
|
||||
}
|
||||
|
||||
DisplayUtils.setLightStatusBar(this, !mBlackTheme);
|
||||
DisplayUtils.setStatusBarColor(this, R.color.transparent, !mBlackTheme);
|
||||
findViewById(R.id.pieceMediaControl).setBackgroundResource(R.color.transparent);
|
||||
|
||||
TextView confirmTv = findViewById(R.id.confirmTv);
|
||||
confirmTv.setOnClickListener(v -> {
|
||||
submitResultAndFinish();
|
||||
});
|
||||
confirmTv.setEnabled(true);
|
||||
confirmTv.setAlpha(1F);
|
||||
TextView previewTv = findViewById(R.id.previewTv);
|
||||
previewTv.setText(R.string.cancel);
|
||||
previewTv.setOnClickListener(v -> finish());
|
||||
|
||||
DisplayUtils.transparentStatusBar(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int provideNavigationIcon() {
|
||||
return mBlackTheme ? R.drawable.ic_toolbar_back_white : R.drawable.ic_bar_back;
|
||||
}
|
||||
public void submitResultAndFinish() {
|
||||
Intent data = new Intent();
|
||||
String clipPath = getCacheDir().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg";
|
||||
|
||||
@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);
|
||||
data.putExtra(RESULT_CLIP_PATH, clipPath);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void addAssistView(View view) {
|
||||
mCropImageCustom.addAssistView(view);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (reference != null && reference.get() != null) {
|
||||
reference.get().recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus && (reference == null || reference.get() == null)) {
|
||||
ImageView imageView = mCropImageCustom.getCropImageZoomView();
|
||||
Bitmap bitmap = BitmapUtils.getBitmapByFile(getIntent().getStringExtra(EntranceConsts.KEY_PATH),
|
||||
imageView.getWidth(), imageView.getHeight());
|
||||
if (bitmap != null) {
|
||||
reference = new SoftReference<>(bitmap);
|
||||
imageView.setImageBitmap(reference.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package com.gh.gamecenter.game.upload
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.TextPaint
|
||||
@ -29,18 +28,21 @@ import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
|
||||
import com.gh.gamecenter.common.callback.OnListClickListener
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.choosepic.ChoosePicAdapter
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.core.utils.SpanBuilder
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.FragmentGameUploadBinding
|
||||
import com.gh.gamecenter.feature.entity.InstallGameEntity
|
||||
import com.gh.gamecenter.feature.game.SelectGameAdapter
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Util_System_Keyboard
|
||||
import com.lightgame.utils.Utils
|
||||
import com.zhihu.matisse.Matisse
|
||||
import com.zhihu.matisse.internal.utils.PathUtils
|
||||
import io.reactivex.disposables.Disposable
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
@ -86,7 +88,6 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
mViewModel.upLoadSuccess.observe(viewLifecycleOwner, Observer {
|
||||
if (it) {
|
||||
ToastUtils.showToast("上传成功")
|
||||
MtaHelper.onEvent("游戏上传", "游戏上传", "上传成功")
|
||||
mUploadDialog.dismiss()
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
@ -119,17 +120,11 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
5,
|
||||
object : OnListClickListener {
|
||||
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
|
||||
MtaHelper.onEvent("游戏上传", "游戏图片", "添加图片")
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
|
||||
startActivityForResult(intent, MEDIA_STORE_REQUEST)
|
||||
}
|
||||
}
|
||||
) {
|
||||
MtaHelper.onEvent(
|
||||
"游戏上传",
|
||||
"游戏图片",
|
||||
"删除图片"
|
||||
)
|
||||
}
|
||||
mAdapter?.setPicItem(R.layout.game_upload_pic_item)
|
||||
mAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
|
||||
@ -147,19 +142,13 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
1,
|
||||
object : OnListClickListener {
|
||||
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
|
||||
MtaHelper.onEvent("游戏上传", "游戏图标", "添加图片")
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(requireActivity()) {
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
|
||||
startActivityForResult(intent, MEDIA_ICON_STORE_REQUEST)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
MtaHelper.onEvent(
|
||||
"游戏上传",
|
||||
"游戏图标",
|
||||
"删除图片"
|
||||
)
|
||||
}
|
||||
mIconAdapter?.setPicItem(R.layout.game_upload_pic_item)
|
||||
mIconAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
|
||||
@ -194,18 +183,15 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
private fun initListener() {
|
||||
|
||||
mBinding.chooseGameLl.setOnClickListener {
|
||||
MtaHelper.onEvent("游戏上传", "安装包", "点我选择")
|
||||
showSelectDialog()
|
||||
}
|
||||
mBinding.gameIsNetworkingRg.setOnCheckedChangeListener { _, checkedId ->
|
||||
when (checkedId) {
|
||||
R.id.gameNetworkingRb -> {
|
||||
mIsOnline = "yes"
|
||||
MtaHelper.onEvent("游戏上传", "是否联网", "需要联网")
|
||||
}
|
||||
R.id.gameNoNetworkingRb -> {
|
||||
mIsOnline = "no"
|
||||
MtaHelper.onEvent("游戏上传", "是否联网", "无需联网")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -213,15 +199,12 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
when (checkedId) {
|
||||
R.id.gameLanguageChineseRb -> {
|
||||
mGameLanguage = "中文"
|
||||
MtaHelper.onEvent("游戏上传", "游戏语言", "中文")
|
||||
}
|
||||
R.id.gameLanguageEnglishRb -> {
|
||||
mGameLanguage = "英文"
|
||||
MtaHelper.onEvent("游戏上传", "游戏语言", "英文")
|
||||
}
|
||||
R.id.gameLanguageOtherRb -> {
|
||||
mGameLanguage = "其他"
|
||||
MtaHelper.onEvent("游戏上传", "游戏语言", "其他")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,21 +212,17 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
when (checkedId) {
|
||||
R.id.gameTypeLocalRb -> {
|
||||
mGameType = "local"
|
||||
MtaHelper.onEvent("游戏上传", "游戏类型", "单机")
|
||||
}
|
||||
R.id.gameTypeOnlineRb -> {
|
||||
mGameType = "online"
|
||||
MtaHelper.onEvent("游戏上传", "游戏类型", "网游")
|
||||
}
|
||||
R.id.gameTypeOtherRb -> {
|
||||
mGameType = "other"
|
||||
MtaHelper.onEvent("游戏上传", "游戏类型", "其他")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mBinding.addGameLabeTv.setOnClickListener {
|
||||
MtaHelper.onEvent("游戏上传", "游戏标签", "添加标签")
|
||||
if (mTags.size < mMaxTagSize) {
|
||||
showAddTagDialog()
|
||||
} else {
|
||||
@ -256,7 +235,6 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
}
|
||||
|
||||
private fun commit() {
|
||||
MtaHelper.onEvent("游戏上传", "提交", "提交")
|
||||
if (TextUtils.isEmpty(mBinding.tvChoose.text.toString())) {
|
||||
ToastUtils.showToast("请先选择游戏安装包")
|
||||
return
|
||||
@ -471,11 +449,9 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
}
|
||||
|
||||
back.setOnClickListener {
|
||||
MtaHelper.onEvent("游戏上传", "安装包", "关闭")
|
||||
mSelectGameDialog?.cancel()
|
||||
}
|
||||
manualBtn.setOnClickListener {
|
||||
MtaHelper.onEvent("游戏上传", "安装包", "从设备上选择")
|
||||
val intent = CleanApkActivity.getIntent(requireContext(), true)
|
||||
PermissionHelper.checkManageAllFilesOrStoragePermissionBeforeAction(requireActivity()) {
|
||||
startActivityForResult(intent, CHOOSE_LOCAL_APK)
|
||||
@ -499,32 +475,20 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (data == null) return
|
||||
if (requestCode == MEDIA_STORE_REQUEST || requestCode == MEDIA_ICON_STORE_REQUEST) {
|
||||
val selectedImage = data.data ?: return
|
||||
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
|
||||
val selectedPaths = Matisse.obtainResult(data) ?: return
|
||||
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
|
||||
|
||||
val cursor = requireContext().contentResolver.query(selectedImage, filePathColumn, null, null, null)
|
||||
?: return
|
||||
cursor.moveToFirst()
|
||||
Utils.log("picturePath = $picturePath")
|
||||
|
||||
try {
|
||||
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
|
||||
val picturePath = cursor.getString(columnIndex)
|
||||
cursor.close()
|
||||
|
||||
Utils.log("picturePath = $picturePath")
|
||||
|
||||
val file = File(picturePath)
|
||||
if (file.length() > 5 * 1024 * 1024) {
|
||||
ToastUtils.showToast(getString(R.string.pic_max_hint, 5))
|
||||
val file = File(picturePath)
|
||||
if (file.length() > 5 * 1024 * 1024) {
|
||||
ToastUtils.showToast(getString(R.string.pic_max_hint, 5))
|
||||
} else {
|
||||
if (requestCode == MEDIA_STORE_REQUEST) {
|
||||
mAdapter!!.addFileList(picturePath)
|
||||
} else {
|
||||
if (requestCode == MEDIA_STORE_REQUEST) {
|
||||
mAdapter!!.addFileList(picturePath)
|
||||
} else {
|
||||
mIconAdapter!!.addFileList(picturePath)
|
||||
}
|
||||
mIconAdapter!!.addFileList(picturePath)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ToastUtils.showToast(e.message ?: "")
|
||||
}
|
||||
} else if (requestCode == CHOOSE_LOCAL_APK) {
|
||||
val packageName = data.getStringExtra(EntranceConsts.KEY_PACKAGENAME) ?: ""
|
||||
@ -610,7 +574,6 @@ class GameUploadFragment : ToolbarFragment() {
|
||||
labelTv.text = tag
|
||||
labelView.findViewById<View>(R.id.picDelIv).setOnClickListener {
|
||||
if (mTags.contains(tag)) {
|
||||
MtaHelper.onEvent("游戏上传", "游戏标签", "删除标签")
|
||||
mBinding.gameLabelFl.removeView(labelView)
|
||||
mTags.remove(tag)
|
||||
mBinding.gameLabelFl.goneIf(mTags.isEmpty())
|
||||
|
||||
@ -16,12 +16,13 @@ import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.common.utils.PermissionHelper
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.common.utils.setRootBackgroundDrawable
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.databinding.DialogChooseGameCollectionCoverTypeBinding
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity.Companion.REQUEST_CODE_IMAGE
|
||||
import com.gh.gamecenter.qa.editor.LocalMediaActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
|
||||
@ -42,7 +43,7 @@ class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
ChooseType.IMAGE,
|
||||
1,
|
||||
"创建游戏单"
|
||||
), REQUEST_CODE_IMAGE
|
||||
|
||||
@ -10,20 +10,20 @@ import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.CropImageActivity
|
||||
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.entity.ErrorEntity
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.*
|
||||
import com.gh.gamecenter.databinding.FragmentBackgroundPreviewBinding
|
||||
import com.gh.gamecenter.feature.entity.BackgroundImageEntity
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.login.user.UserViewModel
|
||||
import com.gh.gamecenter.qa.editor.LocalMediaActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.zhihu.matisse.Matisse
|
||||
import io.reactivex.Single
|
||||
@ -282,7 +282,7 @@ class BackgroundPreviewFragment : ToolbarFragment() {
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
ChooseType.IMAGE,
|
||||
1,
|
||||
"个性背景"
|
||||
), MEDIA_STORE_REQUEST
|
||||
|
||||
@ -12,12 +12,13 @@ import com.gh.gamecenter.CropImageActivity
|
||||
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.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.common.utils.PermissionHelper
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
|
||||
import com.gh.gamecenter.databinding.PersonalityBackgroundFragmentBinding
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.gh.gamecenter.login.user.UserViewModel
|
||||
import com.gh.gamecenter.qa.editor.LocalMediaActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.zhihu.matisse.Matisse
|
||||
|
||||
@ -75,7 +76,7 @@ class PersonalityBackgroundFragment : ToolbarFragment() {
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
ChooseType.IMAGE,
|
||||
1,
|
||||
"个性背景"
|
||||
), MEDIA_STORE_REQUEST
|
||||
|
||||
@ -28,6 +28,7 @@ import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts.KEY_COMMENT_ID
|
||||
import com.gh.gamecenter.common.eventbus.EBReuse
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.common.syncpage.SyncDataEntity
|
||||
import com.gh.gamecenter.common.syncpage.SyncFieldConstants
|
||||
import com.gh.gamecenter.common.syncpage.SyncPageRepository
|
||||
@ -38,10 +39,10 @@ import com.gh.gamecenter.databinding.ItemCommentEditImageBinding
|
||||
import com.gh.gamecenter.eventbus.EBCommentSuccess
|
||||
import com.gh.gamecenter.feature.entity.CommentEntity
|
||||
import com.gh.gamecenter.feature.eventbus.EBDeleteComment
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_ID
|
||||
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_TITLE
|
||||
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.QUESTION_ID
|
||||
import com.gh.gamecenter.qa.editor.LocalMediaActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Util_System_Keyboard
|
||||
import com.lightgame.utils.Utils
|
||||
@ -494,7 +495,7 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
|
||||
val maxChooseCount = 9 - mViewModel.pictureList.size
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
ChooseType.IMAGE,
|
||||
maxChooseCount,
|
||||
"评论列表"
|
||||
)
|
||||
@ -521,11 +522,6 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
|
||||
return
|
||||
}
|
||||
|
||||
if (mShowInputOnly) {
|
||||
// Fuck pm
|
||||
MtaHelper.onEvent("帖子详情", "评论详情-全部回复", "发送")
|
||||
}
|
||||
|
||||
mViewModel.postPictureAndComment(content, mCommentEntity)
|
||||
}
|
||||
|
||||
|
||||
@ -1,148 +0,0 @@
|
||||
package com.gh.gamecenter.qa.editor
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ImageView
|
||||
import android.widget.PopupWindow
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.zhihu.matisse.Matisse
|
||||
import com.zhihu.matisse.MimeType
|
||||
import com.zhihu.matisse.internal.entity.Album
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec
|
||||
import com.zhihu.matisse.internal.model.AlbumCollection
|
||||
|
||||
/**
|
||||
* 选择本地视频/图片
|
||||
*/
|
||||
class LocalMediaActivity : ToolBarActivity(), AlbumCollection.AlbumCallbacks {
|
||||
private var mLocalMediaFragment: LocalMediaFragment? = null
|
||||
private lateinit var mAlbumsSpinner: VideoAlbumsSpanner
|
||||
private lateinit var mAlbumsAdapter: VideoAlbumsAdapter
|
||||
private val mAlbumCollection = AlbumCollection()
|
||||
private var mIsFirstAlbumLoad = true
|
||||
private var mChooseType = ""
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.activity_video_tablayout_viewpager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
mChooseType = intent.getStringExtra(EntranceConsts.KEY_TYPE) ?: ""
|
||||
if (mChooseType == ChooseType.VIDEO.value) {
|
||||
setNavigationTitle("本地视频")
|
||||
} else {
|
||||
setNavigationTitle("本地图片")
|
||||
}
|
||||
mLocalMediaFragment = LocalMediaFragment().apply { arguments = intent.extras }
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, mLocalMediaFragment!!, LocalMediaFragment::class.java.name)
|
||||
.commitAllowingStateLoss()
|
||||
initAlbumsSpinner()
|
||||
mTitleTv.setOnClickListener {
|
||||
mAlbumsSpinner.show(findViewById<View>(R.id.container).height)
|
||||
setPhotoNavigationTitle(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initAlbumsSpinner() {
|
||||
mAlbumsSpinner = VideoAlbumsSpanner(this)
|
||||
mAlbumsAdapter = VideoAlbumsAdapter(this)
|
||||
|
||||
mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.normal_toolbar))
|
||||
mAlbumsSpinner.setAdapter(mAlbumsAdapter)
|
||||
mAlbumsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
mAlbumCollection.setStateCurrentSelection(position)
|
||||
mAlbumsAdapter.cursor.moveToPosition(position)
|
||||
val album = Album.valueOf(mAlbumsAdapter.cursor)
|
||||
if (album.isAll && SelectionSpec.getInstance().capture) {
|
||||
album.addCaptureCount()
|
||||
}
|
||||
if (mLocalMediaFragment?.isAdded == true) {
|
||||
mLocalMediaFragment?.loadVideos(album)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mAlbumsSpinner.setDismissListener(PopupWindow.OnDismissListener {
|
||||
setPhotoNavigationTitle(false)
|
||||
})
|
||||
//必须加这行代码,[SelectionSpec]是单例模式,下次使用必须先更新MimeType
|
||||
val mimeType = if (mChooseType == ChooseType.VIDEO.value) {
|
||||
MimeType.ofVideo()
|
||||
} else {
|
||||
MimeType.ofImage()
|
||||
}
|
||||
val maxChooseCount = intent.getIntExtra(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
|
||||
Matisse.from(this).choose(mimeType).showSingleMediaType(true).maxSelectable(maxChooseCount)
|
||||
mAlbumCollection.onCreate(this, this)
|
||||
mAlbumCollection.loadAlbums()
|
||||
}
|
||||
|
||||
override fun onAlbumLoad(cursor: Cursor?) {
|
||||
if (mIsFirstAlbumLoad) {
|
||||
mIsFirstAlbumLoad = false
|
||||
mAlbumsAdapter.swapCursor(cursor)
|
||||
mBaseHandler.post {
|
||||
cursor?.moveToPosition(mAlbumCollection.currentSelection)
|
||||
val album = Album.valueOf(cursor)
|
||||
if (album.isAll && SelectionSpec.getInstance().capture) {
|
||||
album.addCaptureCount()
|
||||
}
|
||||
if (mLocalMediaFragment?.isAdded == true) {
|
||||
mLocalMediaFragment?.loadVideos(album)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onAlbumReset() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun setPhotoNavigationTitle(up: Boolean) {
|
||||
val drawable = ContextCompat.getDrawable(
|
||||
HaloApp.getInstance().application,
|
||||
if (up) R.drawable.ic_video_arrow_up else R.drawable.ic_video_arrow_down
|
||||
)
|
||||
val arrowIv = findViewById<ImageView>(R.id.arrowIv)
|
||||
arrowIv?.setImageDrawable(drawable)
|
||||
}
|
||||
|
||||
override fun isAutoResetViewBackgroundEnabled(): Boolean = true
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context, chooseType: ChooseType, maxChooseCount: Int = 1, entrance: String): Intent {
|
||||
return Intent(context, LocalMediaActivity::class.java).apply {
|
||||
putExtra(EntranceConsts.KEY_TYPE, chooseType.value)
|
||||
putExtra(EntranceConsts.KEY_CHOOSE_MAX_COUNT, maxChooseCount)
|
||||
putExtra(EntranceConsts.KEY_ENTRANCE, entrance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ChooseType(val value: String) {
|
||||
VIDEO("video"),
|
||||
IMAGE("image")
|
||||
}
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
package com.gh.gamecenter.qa.editor
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.databinding.LocalVideoItemBinding
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
import com.zhihu.matisse.internal.ui.adapter.RecyclerViewCursorAdapter
|
||||
import com.zhihu.matisse.internal.utils.PathUtils
|
||||
|
||||
class LocalMediaAdapter(
|
||||
val context: Context,
|
||||
val mChooseType: String,
|
||||
val maxChooseSize: Int,
|
||||
val entrance: String,
|
||||
val callback: (ArrayList<Item>) -> Unit
|
||||
) : RecyclerViewCursorAdapter<LocalVideoPreviewViewHolder>(null) {
|
||||
private val mSelectedMediaList = arrayListOf<Item>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalVideoPreviewViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.local_video_item, parent, false)
|
||||
return LocalVideoPreviewViewHolder(LocalVideoItemBinding.bind(view))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: LocalVideoPreviewViewHolder, cursor: Cursor?, position: Int) {
|
||||
val item = Item.valueOf(cursor)
|
||||
holder.binding.durationTv.goneIf(mChooseType == LocalMediaActivity.ChooseType.IMAGE.value)
|
||||
val path = "file:///${PathUtils.getPath(context, item.contentUri)}"
|
||||
ImageUtils.displayResizeMedia(holder.binding.preview, path, 200, 200)
|
||||
holder.binding.durationTv.text = TimeUtils.formatVideoDuration(item.duration / 1000)
|
||||
val drawable = if (mSelectedMediaList.contains(item)) {
|
||||
if (maxChooseSize == 1) {
|
||||
R.drawable.ic_choose_media_selected.toDrawable()
|
||||
} else {
|
||||
R.drawable.ic_choose_media_bg.toDrawable()
|
||||
}
|
||||
} else {
|
||||
R.drawable.ic_choose_media_normal.toDrawable()
|
||||
}
|
||||
holder.binding.checkImageView.setImageDrawable(drawable)
|
||||
if (mSelectedMediaList.contains(item) && maxChooseSize > 1) {
|
||||
holder.binding.chooseCountTv.visibility = View.VISIBLE
|
||||
holder.binding.chooseCountTv.text = (mSelectedMediaList.indexOf(item) + 1).toString()
|
||||
} else {
|
||||
holder.binding.chooseCountTv.visibility = View.GONE
|
||||
}
|
||||
holder.itemView.setOnClickListener {
|
||||
if (mSelectedMediaList.contains(item)) {
|
||||
mSelectedMediaList.remove(item)
|
||||
notifyDataSetChanged()
|
||||
callback.invoke(mSelectedMediaList)
|
||||
} else {
|
||||
if (maxChooseSize == 1) {
|
||||
mSelectedMediaList.clear()
|
||||
}
|
||||
if (mSelectedMediaList.size < maxChooseSize) {
|
||||
mSelectedMediaList.add(item)
|
||||
notifyDataSetChanged()
|
||||
callback.invoke(mSelectedMediaList)
|
||||
} else {
|
||||
if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) {
|
||||
ToastUtils.showToast("至多选择${maxChooseSize}张图片")
|
||||
} else {
|
||||
ToastUtils.showToast("至多选择${maxChooseSize}条视频")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entrance == "发帖子" || entrance == "发提问帖" || entrance == "发视频帖") {
|
||||
val publishContentType = if (entrance == "发帖子") "帖子" else if (entrance == "发提问帖") "提问帖" else "视频帖"
|
||||
val publishMediaType = if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) "图片" else "视频"
|
||||
NewLogUtils.logChooseMedia("click_radio_button", publishContentType, publishMediaType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int, cursor: Cursor?): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
fun getSelectedMediaList(): ArrayList<Item> {
|
||||
return mSelectedMediaList
|
||||
}
|
||||
}
|
||||
|
||||
class LocalVideoPreviewViewHolder(val binding: LocalVideoItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
@ -1,191 +0,0 @@
|
||||
package com.gh.gamecenter.qa.editor
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.BaseFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.tryWithDefaultCatch
|
||||
import com.gh.gamecenter.common.view.GridSpacingItemDecoration
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
import com.gh.gamecenter.databinding.FragmentLocalMediaBinding
|
||||
import com.gh.gamecenter.entity.LocalVideoEntity
|
||||
import com.zhihu.matisse.internal.entity.Album
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
import com.zhihu.matisse.internal.model.AlbumMediaCollection
|
||||
import com.zhihu.matisse.internal.model.SelectedItemCollection
|
||||
import com.zhihu.matisse.internal.ui.BasePreviewActivity
|
||||
import com.zhihu.matisse.internal.ui.SelectedPreviewActivity
|
||||
import com.zhihu.matisse.internal.utils.PathUtils
|
||||
import com.zhihu.matisse.ui.MatisseActivity
|
||||
|
||||
class LocalMediaFragment : BaseFragment<Any>(), AlbumMediaCollection.AlbumMediaCallbacks {
|
||||
private lateinit var mBinding: FragmentLocalMediaBinding
|
||||
private lateinit var mAdapter: LocalMediaAdapter
|
||||
private var mAlbumMediaCollection: AlbumMediaCollection? = null
|
||||
private var mChooseType = ""
|
||||
|
||||
override fun getLayoutId(): Int = 0
|
||||
|
||||
override fun getInflatedLayout(): View {
|
||||
mBinding = FragmentLocalMediaBinding.inflate(LayoutInflater.from(requireContext()), null, false)
|
||||
return mBinding.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mChooseType = arguments?.getString(EntranceConsts.KEY_TYPE) ?: ""
|
||||
// mBinding.reuseNoneData.reuseNoneDataTv.text = "暂无数据~"
|
||||
mBinding.listRv.layoutManager = GridLayoutManager(requireContext(), 3)
|
||||
mBinding.listRv.addItemDecoration(GridSpacingItemDecoration(3, 4F.dip2px(), false))
|
||||
val maxChooseCount = arguments?.getInt(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
|
||||
mAdapter = LocalMediaAdapter(
|
||||
requireContext(), mChooseType, maxChooseCount
|
||||
?: 1, mEntrance
|
||||
) {
|
||||
mBinding.previewTv.isEnabled = it.isNotEmpty()
|
||||
mBinding.confirmTv.isEnabled = it.isNotEmpty()
|
||||
if (it.isEmpty()) {
|
||||
mBinding.previewTv.setTextColor(R.color.text_instance.toColor(requireContext()))
|
||||
mBinding.confirmTv.alpha = 0.6f
|
||||
} else {
|
||||
mBinding.previewTv.setTextColor(R.color.text_secondary.toColor(requireContext()))
|
||||
mBinding.confirmTv.alpha = 1f
|
||||
}
|
||||
mBinding.numTv.text = "(${it.size}/${mAdapter.maxChooseSize})"
|
||||
}
|
||||
mBinding.numTv.text = "(0/${mAdapter.maxChooseSize})"
|
||||
mBinding.listRv.adapter = mAdapter
|
||||
mBinding.listRefresh.isEnabled = false
|
||||
|
||||
val publishContentType = if (mEntrance == "发帖子") "帖子" else if (mEntrance == "发提问帖") "提问帖" else "视频帖"
|
||||
val publishMediaType = if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) "图片" else "视频"
|
||||
mBinding.previewTv.setOnClickListener {
|
||||
if (mChooseType == LocalMediaActivity.ChooseType.VIDEO.value) {
|
||||
val intent = PreviewVideoActivity.getIntent(requireContext(), mAdapter.getSelectedMediaList())
|
||||
requireActivity().startActivityForResult(intent, PREVIEW_VIDEO)
|
||||
NewLogUtils.logChooseMedia("click_preview", publishContentType, publishMediaType)
|
||||
} else {
|
||||
val intent = Intent(requireContext(), SelectedPreviewActivity::class.java)
|
||||
val bundle = bundleOf(
|
||||
SelectedItemCollection.STATE_SELECTION to mAdapter.getSelectedMediaList(),
|
||||
SelectedItemCollection.STATE_COLLECTION_TYPE to SelectedItemCollection.COLLECTION_IMAGE
|
||||
)
|
||||
intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, bundle)
|
||||
startActivityForResult(intent, PREVIEW_IMAGE)
|
||||
}
|
||||
|
||||
}
|
||||
mBinding.confirmTv.setOnClickListener {
|
||||
NewLogUtils.logChooseMedia("click_confirm", publishContentType, publishMediaType)
|
||||
val intent = Intent()
|
||||
if (mChooseType == LocalMediaActivity.ChooseType.VIDEO.value) {
|
||||
val localVideoList = arrayListOf<LocalVideoEntity>()
|
||||
mAdapter.getSelectedMediaList().forEach {
|
||||
val path = PathUtils.getPath(requireContext(), it.contentUri)
|
||||
if (path == null) {
|
||||
toast("视频已不存在,请重新选择")
|
||||
return@forEach
|
||||
}
|
||||
val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis()
|
||||
val format = getFileFormat(it.mimeType)
|
||||
localVideoList.add(
|
||||
LocalVideoEntity(
|
||||
id,
|
||||
path,
|
||||
contentUri = it.contentUri,
|
||||
duration = it.duration,
|
||||
format = format,
|
||||
size = it.size
|
||||
)
|
||||
)
|
||||
}
|
||||
intent.putExtra(LocalVideoEntity::class.java.name, localVideoList)
|
||||
} else {
|
||||
val data = mAdapter.getSelectedMediaList().map { it.contentUri }.toList()
|
||||
val path = data.map { PathUtils.getPath(requireContext(), it) }.toList()
|
||||
intent.putParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION, ArrayList<Uri>(data))
|
||||
intent.putStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH, ArrayList<String>(path))
|
||||
}
|
||||
requireActivity().setResult(Activity.RESULT_OK, intent)
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileFormat(mimeType: String?): String {
|
||||
var format = ""
|
||||
tryWithDefaultCatch {
|
||||
if (mimeType != null) {
|
||||
val split = mimeType.split("/")
|
||||
format = if (split.count() >= 2) {
|
||||
split[1]
|
||||
} else {
|
||||
mimeType
|
||||
}
|
||||
}
|
||||
}
|
||||
return format
|
||||
}
|
||||
|
||||
override fun onAlbumMediaReset() {
|
||||
mAdapter.swapCursor(null)
|
||||
}
|
||||
|
||||
override fun onAlbumMediaLoad(cursor: Cursor?) {
|
||||
mAdapter.swapCursor(cursor)
|
||||
mBinding.reuseNoneData.reuseNoneData.visibility = View.GONE
|
||||
mBinding.reuseLlLoading.root.visibility = View.GONE
|
||||
mBinding.reuseNoConnection.root.visibility = View.GONE
|
||||
mBinding.listRefresh.isRefreshing = false
|
||||
}
|
||||
|
||||
fun loadVideos(album: Album) {
|
||||
mAlbumMediaCollection?.onDestroy()
|
||||
|
||||
mAlbumMediaCollection = AlbumMediaCollection()
|
||||
mAlbumMediaCollection?.onCreate(requireActivity(), this@LocalMediaFragment)
|
||||
|
||||
mAlbumMediaCollection?.load(album)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (data == null) return
|
||||
if (requestCode == PREVIEW_IMAGE) {
|
||||
val bundleExtra = data.getBundleExtra(BasePreviewActivity.EXTRA_RESULT_BUNDLE)
|
||||
val resultApply = data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_APPLY, false)
|
||||
val items = bundleExtra?.getParcelableArrayList<Item>(SelectedItemCollection.STATE_SELECTION)
|
||||
if (resultApply && !items.isNullOrEmpty()) {
|
||||
mAdapter.getSelectedMediaList().clear()
|
||||
mAdapter.getSelectedMediaList().addAll(items)
|
||||
mAdapter.notifyDataSetChanged()
|
||||
mBinding.numTv.text = "(${items.size}/${mAdapter.maxChooseSize})"
|
||||
}
|
||||
|
||||
} else if (requestCode == PREVIEW_VIDEO) {
|
||||
requireActivity().setResult(Activity.RESULT_OK, data)
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mAlbumMediaCollection?.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREVIEW_VIDEO = 100
|
||||
const val PREVIEW_IMAGE = 101
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,12 +3,15 @@ package com.gh.gamecenter.qa.editor
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
|
||||
@Route(path = RouteConsts.activity.previewVideoActivity)
|
||||
class PreviewVideoActivity : BaseActivity() {
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.activity_amway
|
||||
|
||||
@ -22,7 +22,7 @@ import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.*
|
||||
import com.gh.gamecenter.databinding.FragmentPreviewVideoBinding
|
||||
import com.gh.gamecenter.databinding.ItemVideoSelectorBinding
|
||||
import com.gh.gamecenter.entity.LocalVideoEntity
|
||||
import com.gh.gamecenter.common.entity.LocalVideoEntity
|
||||
import com.gh.gamecenter.video.poster.PosterEditActivity
|
||||
import com.gh.gamecenter.video.upload.view.UploadVideoActivity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
@ -24,8 +24,10 @@ 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.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
|
||||
@ -34,6 +36,7 @@ import com.gh.gamecenter.databinding.FragmentVideoPublishBinding
|
||||
import com.gh.gamecenter.entity.*
|
||||
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.login.user.UserManager
|
||||
import com.gh.gamecenter.qa.BbsType
|
||||
import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment
|
||||
@ -41,7 +44,6 @@ 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.qa.editor.LocalMediaActivity
|
||||
import com.gh.gamecenter.video.poster.PosterEditActivity
|
||||
import com.gh.gamecenter.video.upload.OnUploadListener
|
||||
import com.gh.gamecenter.video.upload.UploadManager
|
||||
@ -117,7 +119,7 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.VIDEO,
|
||||
ChooseType.VIDEO,
|
||||
1,
|
||||
"发视频帖"
|
||||
), BaseRichEditorActivity.INSERT_MEDIA_VIDEO_CODE
|
||||
@ -285,7 +287,7 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.VIDEO,
|
||||
ChooseType.VIDEO,
|
||||
1,
|
||||
"发视频帖"
|
||||
), BaseRichEditorActivity.INSERT_MEDIA_VIDEO_CODE
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.halo.assistant.fragment.user
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
@ -13,7 +12,6 @@ import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.core.utils.EmptyCallback
|
||||
import com.gh.gamecenter.common.utils.PermissionHelper
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.common.utils.enlargeTouchArea
|
||||
@ -22,6 +20,8 @@ import com.gh.gamecenter.databinding.FragmentManuallyRealNameBinding
|
||||
import com.gh.gamecenter.login.entity.IdCardEntity
|
||||
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
|
||||
import com.gh.gamecenter.common.utils.UploadImageUtils
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.squareup.picasso.MemoryPolicy
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
@ -150,22 +150,13 @@ class ManuallyRealNameFragment : ToolbarFragment() {
|
||||
}
|
||||
|
||||
private fun selectImage() {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
startActivityForResult(intent, REQUEST_IMAGE)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// https://stackoverflow.com/questions/45707678
|
||||
val getIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
getIntent.type = "image/*"
|
||||
|
||||
val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
pickIntent.type = "image/*"
|
||||
|
||||
val chooserIntent = Intent.createChooser(getIntent, "Select Image")
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(pickIntent))
|
||||
|
||||
startActivityForResult(chooserIntent, REQUEST_IMAGE)
|
||||
}
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
ChooseType.IMAGE,
|
||||
1,
|
||||
"人工审核"
|
||||
)
|
||||
startActivityForResult(intent, REQUEST_IMAGE)
|
||||
}
|
||||
|
||||
private fun removeUploadedImage() {
|
||||
|
||||
@ -4,20 +4,23 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
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.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.CropMaskFrameView;
|
||||
import com.gh.gamecenter.common.view.CropMaskView;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -65,119 +68,129 @@ public class UserPortraitCropImageActivity extends CropImageActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
sp = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
// CropMaskView cropMaskView = findViewById(R.id.cropMaskView);
|
||||
// CropMaskFrameView cropMaskFrameView = findViewById(R.id.cropMaskFrameView);
|
||||
// cropMaskView.updateMaskColor(false);
|
||||
// cropMaskView.updateMaskShape(true);
|
||||
// cropMaskFrameView.updateMaskShape(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
if (item.getItemId() == R.id.layout_menu_positive) {
|
||||
final WaitingDialogFragment postDialog = WaitingDialogFragment.newInstance(getString(R.string.post_img));
|
||||
postDialog.show(getSupportFragmentManager(), null);
|
||||
final String path = getCacheDir() + File.separator + System.currentTimeMillis() + ".jpg";
|
||||
Observable.create((ObservableOnSubscribe<String>) emitter -> {
|
||||
boolean isSuccess = savePicture(path);
|
||||
if (isSuccess) {
|
||||
UploadImageUtils.INSTANCE.uploadImage(UploadImageUtils.UploadType.icon, path, new UploadImageUtils.OnUploadImageListener() {
|
||||
@Override
|
||||
public void onSuccess(@NotNull String imageUrl) {
|
||||
emitter.onNext(imageUrl);
|
||||
emitter.onComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable Throwable e) {
|
||||
if (e != null) {
|
||||
emitter.onError(e);
|
||||
} else {
|
||||
emitter.onError(new IllegalStateException("upload image error"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long total, long progress) {
|
||||
int percent = (int) (100 * (progress / (float) total));
|
||||
if (percent >= 100) percent = 99;
|
||||
if (postDialog != null) {
|
||||
postDialog.uploadWaitingHint("图片上传中 " + percent + "%");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(new Response<String>() {
|
||||
@Override
|
||||
public void onResponse(String url) {
|
||||
try {
|
||||
if (postDialog != null) postDialog.dismissAllowingStateLoss();
|
||||
mBaseHandler.sendEmptyMessage(0);
|
||||
|
||||
String iconCount = sp.getString("updateIconCount", null);
|
||||
|
||||
long l = System.currentTimeMillis();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
|
||||
String time = format.format(new Date(l));
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("time", time);
|
||||
|
||||
if (TextUtils.isEmpty(iconCount)) {
|
||||
jsonObject.put("count", 1);
|
||||
} else {
|
||||
JSONObject json = new JSONObject(iconCount);
|
||||
String lastTime = json.getString("time");
|
||||
if (lastTime.equals(time)) {
|
||||
jsonObject.put("count", json.getInt("count") + 1);
|
||||
} else {
|
||||
jsonObject.put("count", 1);
|
||||
}
|
||||
}
|
||||
|
||||
sp.edit().putString("updateIconCount", jsonObject.toString()).apply();
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(EntranceConsts.KEY_URL, url);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
public void submitResultAndFinish() {
|
||||
final WaitingDialogFragment postDialog = WaitingDialogFragment.newInstance(getString(R.string.post_img));
|
||||
postDialog.show(getSupportFragmentManager(), null);
|
||||
final String path = getCacheDir() + File.separator + System.currentTimeMillis() + ".jpg";
|
||||
Observable.create((ObservableOnSubscribe<String>) emitter -> {
|
||||
boolean isSuccess = cropBitmapAndSave(path);
|
||||
if (isSuccess) {
|
||||
UploadImageUtils.INSTANCE.uploadImage(UploadImageUtils.UploadType.icon, path, new UploadImageUtils.OnUploadImageListener() {
|
||||
@Override
|
||||
public void onSuccess(@NotNull String imageUrl) {
|
||||
emitter.onNext(imageUrl);
|
||||
emitter.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
@Override
|
||||
public void onError(@Nullable Throwable e) {
|
||||
if (e != null) {
|
||||
emitter.onError(e);
|
||||
} else {
|
||||
emitter.onError(new IllegalStateException("upload image error"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long total, long progress) {
|
||||
int percent = (int) (100 * (progress / (float) total));
|
||||
if (percent >= 100) percent = 99;
|
||||
if (postDialog != null) {
|
||||
postDialog.uploadWaitingHint("图片上传中 " + percent + "%");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(new Response<String>() {
|
||||
@Override
|
||||
public void onResponse(String url) {
|
||||
try {
|
||||
if (postDialog != null) postDialog.dismissAllowingStateLoss();
|
||||
try {
|
||||
if (e != null && e.code() == HttpURLConnection.HTTP_FORBIDDEN && e.response().errorBody() != null) {
|
||||
JSONObject object = new JSONObject(e.response().errorBody().string());
|
||||
String detail = object.getString("detail");
|
||||
if ("too frequent".equals(detail)) {
|
||||
mBaseHandler.sendEmptyMessage(2);
|
||||
} else if ("INVALID PICTURE".equals(detail)) {
|
||||
mBaseHandler.sendEmptyMessage(3);
|
||||
} else {
|
||||
mBaseHandler.sendEmptyMessage(1);
|
||||
}
|
||||
mBaseHandler.sendEmptyMessage(0);
|
||||
|
||||
String iconCount = sp.getString("updateIconCount", null);
|
||||
|
||||
long l = System.currentTimeMillis();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
|
||||
String time = format.format(new Date(l));
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("time", time);
|
||||
|
||||
if (TextUtils.isEmpty(iconCount)) {
|
||||
jsonObject.put("count", 1);
|
||||
} else {
|
||||
JSONObject json = new JSONObject(iconCount);
|
||||
String lastTime = json.getString("time");
|
||||
if (lastTime.equals(time)) {
|
||||
jsonObject.put("count", json.getInt("count") + 1);
|
||||
} else {
|
||||
jsonObject.put("count", 1);
|
||||
}
|
||||
}
|
||||
|
||||
sp.edit().putString("updateIconCount", jsonObject.toString()).apply();
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(EntranceConsts.KEY_URL, url);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
if (postDialog != null) postDialog.dismissAllowingStateLoss();
|
||||
try {
|
||||
if (e != null && e.code() == HttpURLConnection.HTTP_FORBIDDEN && e.response().errorBody() != null) {
|
||||
JSONObject object = new JSONObject(e.response().errorBody().string());
|
||||
String detail = object.getString("detail");
|
||||
if ("too frequent".equals(detail)) {
|
||||
mBaseHandler.sendEmptyMessage(2);
|
||||
} else if ("INVALID PICTURE".equals(detail)) {
|
||||
mBaseHandler.sendEmptyMessage(3);
|
||||
} else {
|
||||
mBaseHandler.sendEmptyMessage(1);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
} else {
|
||||
mBaseHandler.sendEmptyMessage(1);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
mBaseHandler.sendEmptyMessage(1);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
return super.onMenuItemClick(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 用户头像压缩规则
|
||||
public boolean savePicture(String path) {
|
||||
Bitmap clip = mCropImageCustom.clip();
|
||||
/**
|
||||
* 裁剪图片并保存
|
||||
* @param path 目标路径
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean cropBitmapAndSave(String path) {
|
||||
Bitmap background = BitmapUtils.getViewBitmap(findViewById(R.id.iv));
|
||||
Bitmap cropMask = BitmapUtils.getViewBitmap(findViewById(R.id.cropMaskView));
|
||||
|
||||
Bitmap combinedBitmap = BitmapUtils.combineBitmap(background, cropMask, PorterDuff.Mode.XOR);
|
||||
Bitmap cropedBitmap = BitmapUtils.cropOpaqueArea(combinedBitmap);
|
||||
|
||||
try {
|
||||
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(path)));
|
||||
clip.compress(Bitmap.CompressFormat.WEBP, 100, bos);
|
||||
cropedBitmap.compress(Bitmap.CompressFormat.WEBP, 100, bos);
|
||||
bos.flush();
|
||||
bos.close();
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -2,10 +2,8 @@ package com.halo.assistant.fragment.user.avatar
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.text.TextUtils
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
@ -18,15 +16,18 @@ import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.common.utils.PermissionHelper.checkStoragePermissionBeforeAction
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.DialogChangeAvatarBinding
|
||||
import com.gh.gamecenter.feature.selector.ChooseType
|
||||
import com.gh.gamecenter.login.user.UserViewModel
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.halo.assistant.fragment.user.UserPortraitCropImageActivity
|
||||
import com.lightgame.utils.Utils
|
||||
import com.zhihu.matisse.Matisse
|
||||
import com.zhihu.matisse.internal.utils.PathUtils
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
@ -93,8 +94,8 @@ class ChangeAvatarDialog : BaseDialogFragment() {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
when (requestCode) {
|
||||
REQUEST_MEDIA_ICON -> {
|
||||
val selectedImage = data.data ?: return
|
||||
val picturePath = PathUtils.getPath(requireContext(), selectedImage)
|
||||
val selectedPaths = Matisse.obtainResult(data) ?: return
|
||||
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
|
||||
Utils.log("picturePath = $picturePath")
|
||||
// 上传头像
|
||||
val intent = UserPortraitCropImageActivity.getIntent(
|
||||
@ -144,12 +145,9 @@ class ChangeAvatarDialog : BaseDialogFragment() {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
try {
|
||||
startActivityForResult(intent, REQUEST_MEDIA_ICON)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Utils.toast(requireContext(), "找不到图片选择器")
|
||||
}
|
||||
|
||||
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "头像选择")
|
||||
startActivityForResult(intent, REQUEST_MEDIA_ICON)
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
|
||||
|
Before Width: | Height: | Size: 395 B |
@ -13,27 +13,26 @@
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true" />
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:id="@+id/status_bar"
|
||||
<com.github.piasy.biv.view.BigImageView
|
||||
android:id="@+id/iv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<LinearLayout
|
||||
<com.gh.gamecenter.common.view.CropMaskView
|
||||
android:id="@+id/cropMaskView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@+id/status_bar"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<include layout="@layout/reuse_toolbar" />
|
||||
<com.gh.gamecenter.common.view.CropMaskFrameView
|
||||
android:id="@+id/cropMaskFrameView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.gh.gamecenter.common.view.CropImageCustom
|
||||
android:id="@+id/cropimage_custom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black" />
|
||||
</LinearLayout>
|
||||
<include
|
||||
android:id="@+id/pieceMediaControl"
|
||||
layout="@layout/piece_media_control"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
</com.gh.gamecenter.common.view.MaterializedRelativeLayout>
|
||||
@ -1,96 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_surface"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/list_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/previewTv"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:background="@drawable/border_round_stroke_0dot5_eee_999"
|
||||
android:enabled="false"
|
||||
android:gravity="center"
|
||||
android:text="预览"
|
||||
android:textColor="@color/text_instance"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/confirmTv"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="16dp"
|
||||
android:alpha="0.6"
|
||||
android:background="@drawable/button_blue_oval"
|
||||
android:enabled="false"
|
||||
android:gravity="center"
|
||||
android:text="确定"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/numTv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_toLeftOf="@+id/confirmTv"
|
||||
android:text="(0/3)"
|
||||
android:textColor="@color/text_tertiary" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_ll_loading"
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_no_connection"
|
||||
layout="@layout/reuse_no_connection" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_none_data"
|
||||
layout="@layout/reuse_none_data" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_data_exception"
|
||||
layout="@layout/reuse_data_exception" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
@ -132,11 +132,6 @@
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
|
||||
</style>
|
||||
|
||||
<style name="PosterListPopupWindow" parent="@style/Widget.AppCompat.ListPopupWindow">
|
||||
<item name="android:windowElevation">0dp</item>
|
||||
<item name="android:popupBackground">@color/text_28282E</item>
|
||||
</style>
|
||||
|
||||
<style name="game_gallery_icon">
|
||||
<item name="android:layout_width">84dp</item>
|
||||
<item name="android:layout_height">84dp</item>
|
||||
|
||||
@ -123,11 +123,6 @@
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
|
||||
</style>
|
||||
|
||||
<style name="PosterListPopupWindow" parent="@style/Widget.AppCompat.ListPopupWindow">
|
||||
<item name="android:windowElevation">0dp</item>
|
||||
<item name="android:popupBackground">@color/text_28282E</item>
|
||||
</style>
|
||||
|
||||
<style name="game_gallery_icon">
|
||||
<item name="android:layout_width">84dp</item>
|
||||
<item name="android:layout_height">84dp</item>
|
||||
|
||||
@ -29,6 +29,7 @@ import com.gh.gamecenter.common.utils.NotificationHelper.showNotificationHintDia
|
||||
import com.gh.gamecenter.common.view.BugFixedPopupWindow
|
||||
import com.gh.gamecenter.common.view.choosepic.ChoosePicAdapter
|
||||
import com.gh.gamecenter.core.AppExecutor.uiExecutor
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.feedback.R
|
||||
import com.gh.gamecenter.feedback.databinding.FragmentSuggestAppBinding
|
||||
import com.gh.gamecenter.feedback.databinding.LayoutOptionPopupBinding
|
||||
@ -83,11 +84,12 @@ open class SuggestAppFragment : ToolbarFragment() {
|
||||
val picList = data as List<String>
|
||||
if (position == mPicAdapter!!.itemCount - 1 && picList.size < 5) {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(requireContext()) {
|
||||
val intent = Intent(
|
||||
Intent.ACTION_PICK,
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
1,
|
||||
"意见反馈-${getSuggestType()}"
|
||||
)
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
startActivityForResult(
|
||||
intent,
|
||||
MEDIA_STORE_REQUEST
|
||||
@ -101,8 +103,12 @@ open class SuggestAppFragment : ToolbarFragment() {
|
||||
val picList = data as List<*>
|
||||
if (position == mPicAdapter!!.itemCount - 1 && picList.size < 5) {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(requireContext()) {
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
1,
|
||||
"意见反馈-${getSuggestType()}"
|
||||
)
|
||||
startActivityForResult(intent, MEDIA_STORE_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package com.gh.gamecenter.feedback.view.suggest
|
||||
|
||||
import android.content.Intent
|
||||
import android.provider.MediaStore
|
||||
import android.text.Html
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
@ -12,6 +10,7 @@ import com.gh.gamecenter.common.callback.OnListClickListener
|
||||
import com.gh.gamecenter.common.entity.SuggestType
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.choosepic.ChoosePicAdapter
|
||||
import com.gh.gamecenter.feature.selector.LocalMediaActivity
|
||||
import com.gh.gamecenter.feedback.R
|
||||
import com.gh.gamecenter.feedback.databinding.FragmentSuggestCopyrightBinding
|
||||
import com.gh.gamecenter.feedback.entity.ContactType
|
||||
@ -67,8 +66,12 @@ class SuggestCopyrightFragment : SuggestAppFragment() {
|
||||
mCredentialsPicAdapter = ChoosePicAdapter(requireContext(), object : OnListClickListener {
|
||||
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(requireContext()) {
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
1,
|
||||
"意见反馈-${getSuggestType()}"
|
||||
)
|
||||
startActivityForResult(intent, MEDIA_STORE_CREDENTIALS_REQUEST)
|
||||
}
|
||||
}
|
||||
@ -87,8 +90,12 @@ class SuggestCopyrightFragment : SuggestAppFragment() {
|
||||
mAppScreenshotsAdapter = ChoosePicAdapter(requireContext(), object : OnListClickListener {
|
||||
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(requireContext()) {
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
val intent = LocalMediaActivity.getIntent(
|
||||
requireContext(),
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
1,
|
||||
"意见反馈-版权申诉"
|
||||
)
|
||||
startActivityForResult(intent, MEDIA_STORE_SCREENSHOT_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +47,9 @@ public class Item implements Parcelable {
|
||||
public final long size;
|
||||
public final long duration; // only for video, in ms
|
||||
|
||||
public int positionInAlbum = -1;
|
||||
public String albumId = "";
|
||||
|
||||
private Item(long id, String mimeType, long size, long duration) {
|
||||
this.id = id;
|
||||
this.mimeType = mimeType;
|
||||
|
||||
@ -20,6 +20,7 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.IncapableCause;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
@ -36,6 +37,8 @@ import java.util.Set;
|
||||
public class SelectedItemCollection {
|
||||
|
||||
public static final String STATE_SELECTION = "state_selection";
|
||||
public static final String STATE_ALBUM = "state_album";
|
||||
public static final String STATE_DEFAULT_POSITION = "state_default_position";
|
||||
public static final String STATE_COLLECTION_TYPE = "state_collection_type";
|
||||
/**
|
||||
* Empty collection
|
||||
@ -55,6 +58,8 @@ public class SelectedItemCollection {
|
||||
public static final int COLLECTION_MIXED = COLLECTION_IMAGE | COLLECTION_VIDEO;
|
||||
private final Context mContext;
|
||||
private Set<Item> mItems;
|
||||
private Album mAlbum;
|
||||
private int mDefaultPosition = 0;
|
||||
private int mCollectionType = COLLECTION_UNDEFINED;
|
||||
|
||||
public SelectedItemCollection(Context context) {
|
||||
@ -66,6 +71,8 @@ public class SelectedItemCollection {
|
||||
mItems = new LinkedHashSet<>();
|
||||
} else {
|
||||
List<Item> saved = bundle.getParcelableArrayList(STATE_SELECTION);
|
||||
mAlbum = bundle.getParcelable(STATE_ALBUM);
|
||||
mDefaultPosition = bundle.getInt(STATE_DEFAULT_POSITION);
|
||||
mItems = new LinkedHashSet<>(saved);
|
||||
mCollectionType = bundle.getInt(STATE_COLLECTION_TYPE, COLLECTION_UNDEFINED);
|
||||
}
|
||||
@ -77,12 +84,16 @@ public class SelectedItemCollection {
|
||||
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));
|
||||
outState.putParcelable(STATE_ALBUM, mAlbum);
|
||||
outState.putInt(STATE_DEFAULT_POSITION, mDefaultPosition);
|
||||
outState.putInt(STATE_COLLECTION_TYPE, mCollectionType);
|
||||
}
|
||||
|
||||
public Bundle getDataWithBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));
|
||||
bundle.putParcelable(STATE_ALBUM, mAlbum);
|
||||
bundle.putInt(STATE_DEFAULT_POSITION, mDefaultPosition);
|
||||
bundle.putInt(STATE_COLLECTION_TYPE, mCollectionType);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@ -270,6 +270,7 @@ public class EntranceConsts {
|
||||
public static final String KEY_VIDEO_LIST = "video_list";
|
||||
public static final String KEY_CHOOSE_FORUM_TYPE = "choose_forum_type";
|
||||
public static final String KEY_CHOOSE_MAX_COUNT = "choose_max_count";
|
||||
public static final String KEY_QUICK_CHOOSE = "quick_choose";
|
||||
public static final String KEY_COMMENT_COUNT = "comment_count";
|
||||
public static final String KEY_IS_COMMENT_CONVERSATION = "is_comment_conversation";
|
||||
public static final String KEY_PARENT_TAG = "parent_tag";
|
||||
|
||||
@ -28,6 +28,8 @@ object RouteConsts {
|
||||
const val suggestionActivity = "/help/SuggestionActivity"
|
||||
|
||||
const val messageWrapperActivity = "/message/messageWrapperActivity"
|
||||
|
||||
const val previewVideoActivity = "/video/previewVideoActivity"
|
||||
}
|
||||
|
||||
object fragment {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.gh.gamecenter.entity
|
||||
package com.gh.gamecenter.common.entity
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
@ -0,0 +1,38 @@
|
||||
package com.gh.gamecenter.common.structure
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class ParcelableMap<K, V>(
|
||||
val map: HashMap<K, V> = HashMap()
|
||||
) : Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this() {
|
||||
val size = parcel.readInt()
|
||||
for (i in 0 until size) {
|
||||
val key = parcel.readValue(ParcelableMap::class.java.classLoader) as K
|
||||
val value = parcel.readValue(ParcelableMap::class.java.classLoader) as V
|
||||
map[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(map.size)
|
||||
for (entry in map.entries) {
|
||||
parcel.writeValue(entry.key)
|
||||
parcel.writeValue(entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun describeContents(): Int = 0
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<ParcelableMap<*, *>> {
|
||||
override fun createFromParcel(parcel: Parcel): ParcelableMap<*, *> {
|
||||
return ParcelableMap<Any, Any>(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<ParcelableMap<*, *>?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,19 @@ import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.ExifInterface;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.RequiresApi;
|
||||
@ -600,4 +606,73 @@ public class BitmapUtils {
|
||||
context.sendBroadcast(intent);
|
||||
Utils.log("保存分享图片路径:" + imageFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 对两个 bitmap 进行 PorterDuff 操作,并返回一个新的 bitmap
|
||||
* @param mode 你想要操作的模式
|
||||
* @return PorterDuff 处理后的 bitmap
|
||||
*/
|
||||
public static Bitmap combineBitmap(Bitmap sourceBitmap, Bitmap destBitmap, PorterDuff.Mode mode) {
|
||||
int width = Math.max(sourceBitmap.getWidth(), destBitmap.getWidth());
|
||||
int height = Math.max(sourceBitmap.getHeight(), destBitmap.getHeight());
|
||||
|
||||
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(resultBitmap);
|
||||
|
||||
// Draw sourceBitmap first
|
||||
canvas.drawBitmap(sourceBitmap, 0f, 0f, null);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setXfermode(new PorterDuffXfermode(mode));
|
||||
|
||||
// Draw destBitmap on top of sourceBitmap with porterDuff mode
|
||||
canvas.drawBitmap(destBitmap, 0f, 0f, paint);
|
||||
|
||||
return resultBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除 bitmap 中的透明部分,返回一个裁剪后的 bitmap
|
||||
*/
|
||||
public static Bitmap cropOpaqueArea(Bitmap bitmap) {
|
||||
int width = bitmap.getWidth();
|
||||
int height = bitmap.getHeight();
|
||||
|
||||
int minX = width, minY = height, maxX = -1, maxY = -1;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel = bitmap.getPixel(x, y);
|
||||
if (Color.alpha(pixel) == 255) { // Pixel is not opaque
|
||||
if (x < minX) minX = x;
|
||||
if (y < minY) minY = y;
|
||||
if (x > maxX) maxX = x;
|
||||
if (y > maxY) maxY = y;
|
||||
} else {
|
||||
// make all opaque pixels transparent
|
||||
bitmap.setPixel(x, y, Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxX < minX || maxY < minY) {
|
||||
// No non-transparent pixels found, return a blank Bitmap
|
||||
return Bitmap.createBitmap(1, 1, bitmap.getConfig());
|
||||
}
|
||||
|
||||
// Crop the bitmap
|
||||
Rect cropRect = new Rect(minX, minY, maxX + 1, maxY + 1);
|
||||
return Bitmap.createBitmap(bitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 View 的 Bitmap
|
||||
*/
|
||||
public static Bitmap getViewBitmap(View view) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
view.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package com.gh.gamecenter.common.utils
|
||||
|
||||
class FIFOMap<K, V>(private val maxEntries: Int) : LinkedHashMap<K, V>(maxEntries + 1, 1.0f, false) {
|
||||
|
||||
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean {
|
||||
return size > maxEntries
|
||||
}
|
||||
|
||||
}
|
||||
@ -491,11 +491,11 @@ object ImageUtils {
|
||||
|
||||
fun displayResizeMedia(
|
||||
draweeView: SimpleDraweeView,
|
||||
url: String,
|
||||
uri: Uri,
|
||||
resizeWidthDp: Int,
|
||||
resizeHeightDp: Int
|
||||
) {
|
||||
val request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
|
||||
val request = ImageRequestBuilder.newBuilderWithSource(uri)
|
||||
.setResizeOptions(ResizeOptions(resizeWidthDp, resizeHeightDp))
|
||||
.build()
|
||||
val controller = Fresco.newDraweeControllerBuilder()
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
package com.gh.gamecenter.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.view.CropMaskView.Companion.MASK_SHAPE_CIRCLE
|
||||
import com.gh.gamecenter.common.view.CropMaskView.Companion.MASK_SHAPE_SQUARE
|
||||
import com.gh.gamecenter.common.view.CropMaskView.Companion.shapeHorizontalPadding
|
||||
import kotlin.math.min
|
||||
|
||||
class CropMaskFrameView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
View(context, attrs) {
|
||||
|
||||
private var paint: Paint? = null
|
||||
private var maskShape = MASK_SHAPE_CIRCLE
|
||||
|
||||
init {
|
||||
paint = Paint()
|
||||
paint?.isAntiAlias = true
|
||||
}
|
||||
|
||||
fun updateMaskShape(useSquare: Boolean) {
|
||||
maskShape = if (useSquare) MASK_SHAPE_SQUARE else MASK_SHAPE_CIRCLE
|
||||
invalidate()
|
||||
}
|
||||
|
||||
protected override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
var saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG)
|
||||
|
||||
paint?.strokeWidth = 1F.dip2px().toFloat()
|
||||
paint?.color = Color.WHITE
|
||||
paint?.style = Paint.Style.STROKE
|
||||
|
||||
// Draw the circular area with CLEAR mode to make it transparent
|
||||
if (maskShape == MASK_SHAPE_CIRCLE) {
|
||||
var radius = (min(width.toDouble(), height.toDouble()) / 2f).toFloat() - shapeHorizontalPadding
|
||||
canvas.drawCircle(width / 2f, height / 2f, radius, paint!!)
|
||||
} else {
|
||||
val maxSquareSize = min(width, height).toFloat()
|
||||
val startX = shapeHorizontalPadding.toFloat()
|
||||
val startY = (height - maxSquareSize) / 2
|
||||
|
||||
canvas.drawRect(
|
||||
startX,
|
||||
startY,
|
||||
startX + maxSquareSize - 2 * shapeHorizontalPadding,
|
||||
startY + maxSquareSize,
|
||||
paint!!)
|
||||
}
|
||||
|
||||
canvas.restoreToCount(saved)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package com.gh.gamecenter.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffXfermode
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import kotlin.math.min
|
||||
|
||||
class CropMaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
View(context, attrs) {
|
||||
|
||||
private var paint: Paint? = null
|
||||
private var maskColor: Int = defaultMaskColor
|
||||
private var maskShape = MASK_SHAPE_CIRCLE
|
||||
private val xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
|
||||
|
||||
init {
|
||||
paint = Paint()
|
||||
paint?.isAntiAlias = true
|
||||
}
|
||||
|
||||
fun updateMaskColor(useDefault: Boolean) {
|
||||
maskColor = if (useDefault) defaultMaskColor else darkerMaskColor
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun updateMaskShape(useSquare: Boolean) {
|
||||
maskShape = if (useSquare) MASK_SHAPE_SQUARE else MASK_SHAPE_CIRCLE
|
||||
invalidate()
|
||||
}
|
||||
|
||||
protected override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
var saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG)
|
||||
|
||||
// Draw the 50% transparent dark background
|
||||
canvas.drawColor(maskColor)
|
||||
|
||||
// Set the mode to clear to punch out the circle
|
||||
paint?.xfermode = xfermode
|
||||
|
||||
// Draw the circular area with CLEAR mode to make it transparent
|
||||
if (maskShape == MASK_SHAPE_CIRCLE) {
|
||||
var radius = (min(width.toDouble(), height.toDouble()) / 2f).toFloat() - shapeHorizontalPadding
|
||||
canvas.drawCircle(width / 2f, height / 2f, radius, paint!!)
|
||||
} else {
|
||||
val maxSquareSize = min(width, height).toFloat()
|
||||
val startX = shapeHorizontalPadding.toFloat()
|
||||
val startY = (height - maxSquareSize) / 2
|
||||
|
||||
canvas.drawRect(
|
||||
startX,
|
||||
startY,
|
||||
startX + maxSquareSize - 2 * shapeHorizontalPadding,
|
||||
startY + maxSquareSize,
|
||||
paint!!)
|
||||
}
|
||||
|
||||
// Reset the Xfermode to null after drawing the transparent shape
|
||||
paint?.xfermode = null
|
||||
|
||||
canvas.restoreToCount(saved)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val defaultMaskColor: Int = Color.argb(0x80, 0, 0, 0)
|
||||
private val darkerMaskColor: Int = Color.argb(0xCC, 0, 0, 0)
|
||||
|
||||
internal val shapeHorizontalPadding = 16F.dip2px()
|
||||
|
||||
internal const val MASK_SHAPE_SQUARE = 0
|
||||
internal const val MASK_SHAPE_CIRCLE = 1
|
||||
}
|
||||
}
|
||||
@ -90,6 +90,8 @@ class DraggableBigImageView @JvmOverloads constructor(
|
||||
mListener?.onRelease(this, scaleX)
|
||||
} else {
|
||||
val offsetY = translationY
|
||||
if (offsetY == 0F) return
|
||||
|
||||
val fraction = min(1F, offsetY / height)
|
||||
mListener?.onRestore(this, fraction)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 396 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="999dp" />
|
||||
<solid android:color="@color/black_alpha_50" />
|
||||
</shape>
|
||||
10
module_common/src/main/res/drawable/ic_check.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
<path
|
||||
android:fillColor="#2496FF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M15.905,4.345C16.198,4.638 16.198,5.112 15.905,5.405L7.28,14.03C6.987,14.323 6.513,14.323 6.22,14.03L2.095,9.905C1.802,9.612 1.802,9.138 2.095,8.845C2.388,8.552 2.862,8.552 3.155,8.845L6.75,12.439L14.845,4.345C15.138,4.052 15.612,4.052 15.905,4.345Z"/>
|
||||
</vector>
|
||||
14
module_common/src/main/res/drawable/ic_function_close.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h24v24h-24z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M4.793,4.793C5.183,4.402 5.817,4.402 6.207,4.793L12,10.586L17.793,4.793C18.183,4.402 18.817,4.402 19.207,4.793C19.598,5.183 19.598,5.817 19.207,6.207L13.414,12L19.207,17.793C19.598,18.183 19.598,18.817 19.207,19.207C18.817,19.598 18.183,19.598 17.793,19.207L12,13.414L6.207,19.207C5.817,19.598 5.183,19.598 4.793,19.207C4.402,18.817 4.402,18.183 4.793,17.793L10.586,12L4.793,6.207C4.402,5.817 4.402,5.183 4.793,4.793Z"/>
|
||||
</group>
|
||||
</vector>
|
||||
18
module_common/src/main/res/drawable/ic_pin_down.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillAlpha="0.1"
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M11,7L8,10L5,7"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeAlpha="0.6"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"/>
|
||||
</vector>
|
||||
@ -1,72 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<com.gh.gamecenter.common.view.MaterializedConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_background_fixed_dark"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/normal_toolbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/appbar_height">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/normal_toolbar"
|
||||
style="@style/Base_ToolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:contentInsetEnd="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:navigationIcon="@null">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/backContainer"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:srcCompat="@drawable/ic_bar_back" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/normal_title"
|
||||
style="@style/toolbar_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_marginRight="50dp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrowIv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginLeft="-42dp"
|
||||
android:layout_toRightOf="@+id/normal_title"
|
||||
android:src="@drawable/ic_video_arrow_down" />
|
||||
</RelativeLayout>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.gh.gamecenter.common.view.MaterializedConstraintLayout>
|
||||
@ -17,10 +17,20 @@
|
||||
app:roundingBorderColor="@color/black_alpha_10"
|
||||
app:roundingBorderWidth="0.5dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/previewMask"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/black_alpha_50"
|
||||
app:layout_constraintBottom_toBottomOf="@id/preview"
|
||||
app:layout_constraintEnd_toEndOf="@id/preview"
|
||||
app:layout_constraintStart_toStartOf="@id/preview"
|
||||
app:layout_constraintTop_toTopOf="@id/preview" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkImageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:src="@drawable/ic_choose_media_normal"
|
||||
@ -33,8 +43,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:text="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
android:textColor="@color/text_aw_primary"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/checkImageView"
|
||||
app:layout_constraintEnd_toEndOf="@+id/checkImageView"
|
||||
@ -43,13 +53,15 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/durationTv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="7dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@drawable/background_shape_black_alpha_50_radius_999"
|
||||
android:text="00:00"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/text_aw_primary"
|
||||
android:textSize="11sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,15 +1,17 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:background="@color/ui_surface">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@color/ui_surface_fixed_dark">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/album_cover"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp" />
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
app:roundedCornerRadius="4dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_name"
|
||||
@ -22,7 +24,7 @@
|
||||
android:layout_toRightOf="@id/album_cover"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textColor="@color/text_aw_primary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
@ -37,13 +39,23 @@
|
||||
android:layout_toRightOf="@id/album_cover"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/text_tertiary"
|
||||
android:textColor="@color/text_aw_tertiary"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkIv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:srcCompat="@drawable/ic_check" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_height="1px"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@color/ui_divider" />
|
||||
android:layout_alignStart="@id/album_name"
|
||||
android:background="@color/ui_divider_fixed_dark" />
|
||||
</RelativeLayout>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
|
||||
<style name="DialogWindowTransparent" parent="android:style/Theme.Dialog">
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
@ -112,4 +113,9 @@
|
||||
<item name="android:layout_height">1dp</item>
|
||||
<item name="android:background">@color/ui_background</item>
|
||||
</style>
|
||||
|
||||
<style name="PosterListPopupWindow" parent="@style/Widget.AppCompat.ListPopupWindow">
|
||||
<item name="android:windowElevation">0dp</item>
|
||||
<item name="android:popupBackground">@color/text_28282E</item>
|
||||
</style>
|
||||
</resources>
|
||||
@ -45,16 +45,19 @@
|
||||
<!-- 页面 -->
|
||||
<!-- 背景 -->
|
||||
<color name="ui_background">#F5F5F5</color>
|
||||
<color name="ui_background_fixed_dark">#1A1A1A</color>
|
||||
<!-- 表面 -->
|
||||
<color name="ui_surface">#FFFFFF</color>
|
||||
<color name="ui_surface_45">#73FFFFFF</color>
|
||||
<color name="ui_surface_0">#00FFFFFF</color>
|
||||
<color name="ui_surface_fixed_dark">#232323</color>
|
||||
<!-- 容器-1 -->
|
||||
<color name="ui_container_1">#F8F8F8</color>
|
||||
<!-- 容器-2 -->
|
||||
<color name="ui_container_2">#F5F5F5</color>
|
||||
<!-- 分隔线 -->
|
||||
<color name="ui_divider">#EEEEEE</color>
|
||||
<color name="ui_divider_fixed_dark">#333333</color>
|
||||
<!-- 高亮 -->
|
||||
<color name="ui_highlight">#0A2496FF</color>
|
||||
<!-- 骨架-框架 -->
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Transparent" parent="AppCompatTheme.APP">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowActionBarOverlay">true</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="DialogWindowTransparent" parent="android:style/Theme.Dialog">
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
@ -143,6 +152,11 @@
|
||||
<item name="android:background">@color/ui_background</item>
|
||||
</style>
|
||||
|
||||
<style name="PosterListPopupWindow" parent="@style/Widget.AppCompat.ListPopupWindow">
|
||||
<item name="android:windowElevation">0dp</item>
|
||||
<item name="android:popupBackground">@color/text_28282E</item>
|
||||
</style>
|
||||
|
||||
<!-- 字体 -->
|
||||
<!-- 标题1 -->
|
||||
<style name="TextTitle1">
|
||||
|
||||
@ -2,4 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.feature">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".selector.LocalMediaActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -0,0 +1,55 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
|
||||
import com.gh.gamecenter.common.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
|
||||
import kotlin.apply
|
||||
import kotlin.jvm.java
|
||||
|
||||
/**
|
||||
* 选择本地视频/图片
|
||||
*/
|
||||
class LocalMediaActivity : BaseActivity() {
|
||||
private var localMediaFragment: LocalMediaFragment? = null
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.activity_video_tablayout_viewpager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
updateStatusBarColor(R.color.ui_surface_fixed_dark, R.color.ui_surface_fixed_dark)
|
||||
localMediaFragment = LocalMediaFragment().apply { arguments = intent.extras }
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, localMediaFragment!!, LocalMediaFragment::class.java.name)
|
||||
.commitAllowingStateLoss()
|
||||
|
||||
DisplayUtils.transparentStatusAndNavigation(this)
|
||||
}
|
||||
|
||||
override fun isAutoResetViewBackgroundEnabled(): Boolean = false
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context,
|
||||
chooseType: ChooseType,
|
||||
maxChooseCount: Int = 1,
|
||||
entrance: String): 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, chooseType == ChooseType.IMAGE && maxChooseCount == 1)
|
||||
putExtra(EntranceConsts.KEY_ENTRANCE, entrance)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ChooseType(val value: String) {
|
||||
VIDEO("video"),
|
||||
IMAGE("image")
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
|
||||
import com.gh.gamecenter.common.R
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
|
||||
import com.gh.gamecenter.common.databinding.LocalVideoItemBinding
|
||||
import com.gh.gamecenter.common.utils.FIFOMap
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.enlargeTouchArea
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
import com.zhihu.matisse.internal.ui.adapter.RecyclerViewCursorAdapter
|
||||
|
||||
class LocalMediaAdapter(
|
||||
val context: Context,
|
||||
val chooseType: String,
|
||||
val isQuickChoose: Boolean,
|
||||
val entrance: String,
|
||||
val viewModel: LocalMediaViewModel
|
||||
) : RecyclerViewCursorAdapter<LocalVideoPreviewViewHolder>(null) {
|
||||
|
||||
val fifoMap = FIFOMap<String, View>(21)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalVideoPreviewViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.local_video_item, parent, false)
|
||||
return LocalVideoPreviewViewHolder(LocalVideoItemBinding.bind(view))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: LocalVideoPreviewViewHolder, cursor: Cursor?, position: Int) {
|
||||
val item = Item.valueOf(cursor).also {
|
||||
it.positionInAlbum = position
|
||||
it.albumId = viewModel.getAlbumId()
|
||||
}
|
||||
holder.binding.durationTv.goneIf(chooseType == ChooseType.IMAGE.value)
|
||||
ImageUtils.displayResizeMedia(holder.binding.preview, item.contentUri, 120, 120)
|
||||
holder.binding.durationTv.text = TimeUtils.formatVideoDuration(item.duration / 1000)
|
||||
|
||||
var selectedResource: Int
|
||||
|
||||
if (isQuickChoose) {
|
||||
holder.binding.previewMask.visibility = View.GONE
|
||||
holder.binding.chooseCountTv.visibility = View.GONE
|
||||
holder.binding.checkImageView.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (viewModel.isSelected(item)) {
|
||||
if (viewModel.maxSelectionSize == 1) {
|
||||
holder.binding.chooseCountTv.visibility = View.GONE
|
||||
selectedResource = R.drawable.ic_choose_media_selected
|
||||
} else {
|
||||
holder.binding.chooseCountTv.visibility = View.VISIBLE
|
||||
holder.binding.chooseCountTv.text = (viewModel.indexOfSelected(item) + 1).toString()
|
||||
selectedResource = R.drawable.ic_choose_media_bg
|
||||
}
|
||||
holder.binding.previewMask.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.binding.chooseCountTv.visibility = View.GONE
|
||||
holder.binding.previewMask.visibility = View.GONE
|
||||
selectedResource = R.drawable.ic_choose_media_normal
|
||||
}
|
||||
holder.binding.checkImageView.setImageResource(selectedResource)
|
||||
holder.binding.checkImageView.enlargeTouchArea(8F.dip2px())
|
||||
holder.binding.checkImageView.setOnClickListener {
|
||||
if (viewModel.isSelected(item)) {
|
||||
viewModel.removeSelection(item)
|
||||
notifyItemChanged(position)
|
||||
} else {
|
||||
if (viewModel.addSelection(item)) {
|
||||
notifyItemChanged(position)
|
||||
} else {
|
||||
if (chooseType == ChooseType.IMAGE.value) {
|
||||
ToastUtils.showToast("至多选择${viewModel.maxSelectionSize}张图片")
|
||||
} else {
|
||||
ToastUtils.showToast("至多选择${viewModel.maxSelectionSize}条视频")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isQuickChoose) {
|
||||
holder.binding.previewMask.visibility = View.GONE
|
||||
holder.binding.chooseCountTv.visibility = View.GONE
|
||||
holder.binding.checkImageView.visibility = View.GONE
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
viewModel.addSelection(item)
|
||||
viewModel.postSelectedResult()
|
||||
}
|
||||
} else {
|
||||
holder.itemView.setOnClickListener {
|
||||
viewModel.previewImage(
|
||||
onlySelected = false,
|
||||
positionInAlbum = position,
|
||||
previewItem = item,
|
||||
urlAndViewMap = fifoMap
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fifoMap.put(item.uri.toString(), holder.binding.preview)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int, cursor: Cursor?) = 0
|
||||
|
||||
}
|
||||
|
||||
class LocalVideoPreviewViewHolder(val binding: LocalVideoItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
@ -0,0 +1,339 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.PopupWindow
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.alibaba.android.arouter.launcher.ARouter
|
||||
import com.gh.gamecenter.common.base.fragment.BaseFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.entity.LocalVideoEntity
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.tryWithDefaultCatch
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
|
||||
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.feature.databinding.FragmentLocalMediaBinding
|
||||
import com.zhihu.matisse.Matisse
|
||||
import com.zhihu.matisse.MimeType
|
||||
|
||||
import com.gh.gamecenter.feature.R
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Album
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec
|
||||
import com.zhihu.matisse.internal.model.AlbumCollection
|
||||
import com.zhihu.matisse.internal.model.AlbumMediaCollection
|
||||
import com.zhihu.matisse.internal.utils.PathUtils
|
||||
import com.zhihu.matisse.ui.MatisseActivity
|
||||
import java.util.ArrayList
|
||||
import kotlin.collections.count
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.map
|
||||
import kotlin.collections.toList
|
||||
import kotlin.jvm.java
|
||||
import kotlin.text.split
|
||||
|
||||
class LocalMediaFragment : BaseFragment<Any>(), AlbumMediaCollection.AlbumMediaCallbacks {
|
||||
|
||||
private lateinit var binding: FragmentLocalMediaBinding
|
||||
private lateinit var adapter: LocalMediaAdapter
|
||||
private var albumMediaCollection: AlbumMediaCollection? = null
|
||||
private var chooseType = ""
|
||||
private var maxChooseCount = 1
|
||||
private var isQuickChoose = false
|
||||
private lateinit var viewModel: LocalMediaViewModel
|
||||
|
||||
private lateinit var albumsSpanner: VideoAlbumsSpanner
|
||||
private lateinit var albumsAdapter: VideoAlbumsAdapter
|
||||
private val albumCollection = AlbumCollection()
|
||||
private var isFirstAlbumLoad = true
|
||||
|
||||
override fun getLayoutId(): Int = 0
|
||||
|
||||
override fun getInflatedLayout(): View {
|
||||
binding = FragmentLocalMediaBinding.inflate(LayoutInflater.from(requireContext()), null, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
chooseType = arguments?.getString(EntranceConsts.KEY_TYPE) ?: ""
|
||||
maxChooseCount = savedInstanceState?.getInt(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
|
||||
?: arguments?.getInt(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
|
||||
?: 1
|
||||
isQuickChoose = savedInstanceState?.getBoolean(EntranceConsts.KEY_QUICK_CHOOSE, false)
|
||||
?: arguments?.getBoolean(EntranceConsts.KEY_QUICK_CHOOSE, false)
|
||||
?: false
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
initAlbumsSpinner()
|
||||
|
||||
if (chooseType == ChooseType.VIDEO.value) {
|
||||
binding.normalTitle.text = "本地视频"
|
||||
} else {
|
||||
binding.normalTitle.text = "本地图片"
|
||||
}
|
||||
|
||||
binding.normalTitle.enlargeTouchArea(10F.dip2px())
|
||||
binding.arrowIv.setOnClickListener { binding.normalTitle.performClick() }
|
||||
binding.normalTitle.setOnClickListener {
|
||||
albumsSpanner.show(280F.dip2px())
|
||||
|
||||
performArrowRotateAnimation(isClockWise = true)
|
||||
}
|
||||
|
||||
binding.listRv.layoutManager = GridLayoutManager(requireContext(), 3)
|
||||
binding.listRv.addItemDecoration(GridSpacingItemDecoration(3, 4F.dip2px(), false))
|
||||
|
||||
if (isQuickChoose) {
|
||||
binding.pieceMediaControl.root.visibility = View.GONE
|
||||
}
|
||||
|
||||
viewModel = viewModelProviderFromParent(LocalMediaViewModel.Factory(maxChooseCount, LocalMediaRepository()))
|
||||
|
||||
adapter = LocalMediaAdapter(requireContext(), chooseType, isQuickChoose, mEntrance, viewModel)
|
||||
|
||||
binding.listRv.adapter = adapter
|
||||
binding.listRefresh.isEnabled = false
|
||||
|
||||
viewModel.selectedMediaListLiveData.observe(viewLifecycleOwner) {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
binding.pieceMediaControl.previewTv.setOnClickListener {
|
||||
if (chooseType == ChooseType.VIDEO.value) {
|
||||
ARouter.getInstance().build(RouteConsts.activity.previewVideoActivity)
|
||||
?.withParcelableArrayList(EntranceConsts.KEY_VIDEO_LIST, viewModel.selectedMediaList)
|
||||
?.navigation(requireActivity(), PREVIEW_VIDEO)
|
||||
} else {
|
||||
viewModel.previewImage(onlySelected = true, 0, null, emptyMap())
|
||||
}
|
||||
}
|
||||
|
||||
binding.pieceMediaControl.confirmTv.setOnClickListener {
|
||||
viewModel.postSelectedResult()
|
||||
}
|
||||
|
||||
viewModel.resultLiveData.observe(viewLifecycleOwner) {
|
||||
if (it) {
|
||||
val intent = Intent()
|
||||
if (chooseType == ChooseType.VIDEO.value) {
|
||||
val localVideoList = arrayListOf<LocalVideoEntity>()
|
||||
viewModel.selectedMediaList.forEach {
|
||||
val path = PathUtils.getPath(requireContext(), it.contentUri)
|
||||
if (path == null) {
|
||||
toast("视频已不存在,请重新选择")
|
||||
return@forEach
|
||||
}
|
||||
val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis()
|
||||
val format = getFileFormat(it.mimeType)
|
||||
localVideoList.add(
|
||||
LocalVideoEntity(
|
||||
id,
|
||||
path,
|
||||
contentUri = it.contentUri,
|
||||
duration = it.duration,
|
||||
format = format,
|
||||
size = it.size
|
||||
)
|
||||
)
|
||||
}
|
||||
intent.putExtra(LocalVideoEntity::class.java.name, localVideoList)
|
||||
} else {
|
||||
val data = viewModel.selectedMediaList.map { it.contentUri }.toList()
|
||||
val path = data.map { PathUtils.getPath(requireContext(), it) }.toList()
|
||||
intent.putParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION, ArrayList<Uri>(data))
|
||||
intent.putStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH, ArrayList<String>(path))
|
||||
}
|
||||
requireActivity().setResult(Activity.RESULT_OK, intent)
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.previewImageLiveData.observe(viewLifecycleOwner) {
|
||||
val previewFragment = LocalMediaPreviewFragment.getInstance(
|
||||
selectedItemList = it.previewList,
|
||||
album = it.album,
|
||||
positionInAlbum = it.positionInAlbum,
|
||||
originalUrlAndViewMap = it.urlAndViewMap,
|
||||
previewItem = it.previewItem
|
||||
)
|
||||
|
||||
viewModel.updateAlbumCursor(adapter.cursor)
|
||||
|
||||
parentFragmentManager.beginTransaction()
|
||||
.replace(R.id.layout_activity_content, previewFragment)
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
viewModel.selectedCountLiveData.observe(viewLifecycleOwner) {
|
||||
val isNotEmpty = it > 0
|
||||
|
||||
binding.pieceMediaControl.previewTv.isEnabled = isNotEmpty
|
||||
binding.pieceMediaControl.confirmTv.isEnabled = isNotEmpty
|
||||
if (isNotEmpty) {
|
||||
binding.pieceMediaControl.previewTv.setTextColor(R.color.text_instance.toColor(requireContext()))
|
||||
binding.pieceMediaControl.confirmTv.alpha = 1f
|
||||
binding.pieceMediaControl.confirmTv.text = "确定(${it})"
|
||||
} else {
|
||||
binding.pieceMediaControl.confirmTv.alpha = 0.6f
|
||||
binding.pieceMediaControl.previewTv.setTextColor(R.color.text_secondary.toColor(requireContext()))
|
||||
binding.pieceMediaControl.confirmTv.text = "确定"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
outState.putInt(EntranceConsts.KEY_CHOOSE_MAX_COUNT, maxChooseCount)
|
||||
outState.putBoolean(EntranceConsts.KEY_QUICK_CHOOSE, isQuickChoose)
|
||||
}
|
||||
|
||||
private fun initAlbumsSpinner() {
|
||||
albumsSpanner = VideoAlbumsSpanner(requireContext())
|
||||
albumsAdapter = VideoAlbumsAdapter(requireContext())
|
||||
|
||||
albumsSpanner.setPopupAnchorView(binding.normalToolbar)
|
||||
albumsSpanner.setAdapter(albumsAdapter)
|
||||
albumsSpanner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
albumCollection.setStateCurrentSelection(position)
|
||||
albumsAdapter.cursor.moveToPosition(position)
|
||||
albumsAdapter.updateSelectedPosition(position)
|
||||
val album = Album.valueOf(albumsAdapter.cursor)
|
||||
if (album.isAll && SelectionSpec.getInstance().capture) {
|
||||
album.addCaptureCount()
|
||||
}
|
||||
|
||||
binding.normalTitle.text = album.getDisplayName(requireContext())
|
||||
|
||||
loadMedia(album)
|
||||
}
|
||||
}
|
||||
|
||||
albumsSpanner.setDismissListener(PopupWindow.OnDismissListener {
|
||||
performArrowRotateAnimation(isClockWise = false)
|
||||
})
|
||||
|
||||
// 必须加这行代码,[SelectionSpec]是单例模式,下次使用必须先更新MimeType
|
||||
val mimeType = if (chooseType == ChooseType.VIDEO.value) {
|
||||
MimeType.ofVideo()
|
||||
} else {
|
||||
MimeType.ofImage()
|
||||
}
|
||||
val maxChooseCount = arguments?.getInt(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1) ?: 1
|
||||
Matisse.from(this).choose(mimeType).showSingleMediaType(true).maxSelectable(maxChooseCount)
|
||||
|
||||
albumCollection.onCreate(requireActivity(), object : AlbumCollection.AlbumCallbacks {
|
||||
override fun onAlbumLoad(cursor: Cursor?) {
|
||||
if (isFirstAlbumLoad) {
|
||||
isFirstAlbumLoad = false
|
||||
albumsAdapter.swapCursor(cursor)
|
||||
mBaseHandler.post {
|
||||
cursor?.moveToPosition(albumCollection.currentSelection)
|
||||
val album = Album.valueOf(cursor)
|
||||
if (album.isAll && SelectionSpec.getInstance().capture) {
|
||||
album.addCaptureCount()
|
||||
}
|
||||
loadMedia(album)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAlbumReset() {
|
||||
// do nothing
|
||||
}
|
||||
})
|
||||
albumCollection.loadAlbums()
|
||||
}
|
||||
|
||||
private fun performArrowRotateAnimation(isClockWise: Boolean) {
|
||||
val arrowIv: View = binding.arrowIv
|
||||
|
||||
val startAngle = if (isClockWise) 0F else 180F
|
||||
val endAngle = if (isClockWise) 180F else 0F
|
||||
|
||||
val clockwiseAnimator = ObjectAnimator.ofFloat(arrowIv, "rotation", startAngle, endAngle)
|
||||
clockwiseAnimator.duration = 200
|
||||
clockwiseAnimator.start()
|
||||
}
|
||||
|
||||
private fun getFileFormat(mimeType: String?): String {
|
||||
var format = ""
|
||||
tryWithDefaultCatch {
|
||||
if (mimeType != null) {
|
||||
val split = mimeType.split("/")
|
||||
format = if (split.count() >= 2) {
|
||||
split[1]
|
||||
} else {
|
||||
mimeType
|
||||
}
|
||||
}
|
||||
}
|
||||
return format
|
||||
}
|
||||
|
||||
override fun onAlbumMediaReset() {
|
||||
adapter.swapCursor(null)
|
||||
}
|
||||
|
||||
override fun onAlbumMediaLoad(cursor: Cursor?) {
|
||||
adapter.swapCursor(cursor)
|
||||
binding.reuseNoneData.reuseNoneData.visibility = View.GONE
|
||||
binding.reuseLlLoading.root.visibility = View.GONE
|
||||
binding.reuseNoConnection.root.visibility = View.GONE
|
||||
binding.listRefresh.isRefreshing = false
|
||||
}
|
||||
|
||||
fun loadMedia(album: Album) {
|
||||
albumMediaCollection?.onDestroy()
|
||||
|
||||
albumMediaCollection = AlbumMediaCollection()
|
||||
albumMediaCollection?.onCreate(requireActivity(), this@LocalMediaFragment)
|
||||
|
||||
viewModel.updateAlbum(album)
|
||||
|
||||
albumMediaCollection?.load(album)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (data == null) return
|
||||
if (requestCode == PREVIEW_VIDEO) {
|
||||
requireActivity().setResult(Activity.RESULT_OK, data)
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
albumMediaCollection?.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREVIEW_VIDEO = 100
|
||||
const val PREVIEW_IMAGE = 101
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,892 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.PointF
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.widget.ImageView
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.transition.ChangeBounds
|
||||
import androidx.transition.ChangeTransform
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionListenerAdapter
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.TransitionSet
|
||||
import androidx.transition.TransitionValues
|
||||
import androidx.viewpager.widget.PagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.imagepipeline.core.ImagePipeline
|
||||
import com.gh.gamecenter.common.base.fragment.BaseFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.structure.ParcelableMap
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.enlargeTouchArea
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
|
||||
import com.gh.gamecenter.common.view.DraggableBigImageView
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.core.utils.SimpleImageLoader
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.core.utils.doOnEnd
|
||||
import com.gh.gamecenter.core.utils.doOnStart
|
||||
import com.gh.gamecenter.feature.R
|
||||
import com.gh.gamecenter.feature.databinding.FragmentLocalMediaPreviewBinding
|
||||
import com.github.piasy.biv.view.BigImageView
|
||||
import com.github.piasy.biv.view.FrescoImageViewFactory
|
||||
import com.lightgame.listeners.OnBackPressedListener
|
||||
import com.zhihu.matisse.internal.entity.Album
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
import java.io.File
|
||||
import kotlin.collections.set
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class LocalMediaPreviewFragment : BaseFragment<Any>(), OnPageChangeListener, OnBackPressedListener {
|
||||
|
||||
private var animating = false
|
||||
private var imageRatio = 1F
|
||||
private var viewRatio = 1F
|
||||
|
||||
lateinit var viewPager: ViewPager
|
||||
lateinit var bottomContainer: View
|
||||
lateinit var toolbarContainer: View
|
||||
lateinit var backgroundView: View
|
||||
|
||||
private val binding by lazy { FragmentLocalMediaPreviewBinding.inflate(layoutInflater) }
|
||||
|
||||
private var viewPagerAdapter: ViewImageAdapter? = null
|
||||
private var parentViewModel: LocalMediaViewModel? = null
|
||||
private var viewModel: LocalMediaPreviewViewModel? = null
|
||||
private var imagePipeline: ImagePipeline? = null
|
||||
private var quickSelectAdapter: QuickSelectAdapter? = null
|
||||
|
||||
private var bigImageView: BigImageView? = null
|
||||
private var container: ViewGroup? = null
|
||||
|
||||
private var album: Album? = null
|
||||
private var previewItem: Item? = null
|
||||
private var itemList: ArrayList<Item>? = null
|
||||
private var containerMap = SparseArray<ViewGroup>()
|
||||
|
||||
private var positionInAlbum = 0
|
||||
private var useEnterAndExitAnimation = false
|
||||
|
||||
private var originLeftMap: HashMap<String, Int>? = null
|
||||
private var originTopMap: HashMap<String, Int>? = null
|
||||
private var originHeightMap: HashMap<String, Int>? = null
|
||||
private var originWidthMap: HashMap<String, Int>? = null
|
||||
|
||||
private var originLeft = 0
|
||||
private var originTop = 0
|
||||
private var originHeight = 0
|
||||
private var originWidth = 0
|
||||
private var originCenterX = 0
|
||||
private var originCenterY = 0
|
||||
private var originHeightWidthRatio = 0F
|
||||
private var targetHeight = 0F
|
||||
private var targetWidth = 0F
|
||||
private var scaleX = 0F
|
||||
private var scaleY = 0F
|
||||
private var translationX = 0F
|
||||
private var translationY = 0F
|
||||
private var targetCenterX = 0F
|
||||
private var targetCenterY = 0F
|
||||
|
||||
private var currentDisplayingItem: Item? = null
|
||||
|
||||
private var quickSelectRecyclerView: RecyclerView? = null
|
||||
|
||||
override fun getLayoutId() = R.layout.fragment_local_media_preview
|
||||
override fun getInflatedLayout(): View? = binding.root
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
parentViewModel = viewModelProviderFromParent()
|
||||
viewModel = viewModelProvider(LocalMediaPreviewViewModel.Factory(parentViewModel!!.localMediaRepo))
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
viewPager = binding.imageDetailPage
|
||||
bottomContainer = binding.bottomContainer
|
||||
backgroundView = binding.backgroundView
|
||||
toolbarContainer = binding.toolbarContainer
|
||||
|
||||
quickSelectRecyclerView = binding.quickSelectRecyclerView
|
||||
|
||||
imagePipeline = Fresco.getImagePipeline()
|
||||
|
||||
positionInAlbum = savedInstanceState?.getInt(EntranceConsts.KEY_CURRENTITEM, 0) ?: 0
|
||||
|
||||
arguments?.let {
|
||||
itemList = it.getParcelableArrayList(KEY_ITEM_LIST) ?: arrayListOf()
|
||||
|
||||
album = it.getParcelable(KEY_ALBUM)
|
||||
previewItem = it.getParcelable(KEY_PREVIEW_ITEM)
|
||||
|
||||
positionInAlbum = it.getInt(KEY_POSITION_IN_ALBUM, 0)
|
||||
useEnterAndExitAnimation = it.getBoolean(KEY_USE_ENTER_AND_EXIT_ANIMATION)
|
||||
originLeftMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_LEFT)?.map
|
||||
originTopMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_TOP)?.map
|
||||
originHeightMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_HEIGHT)?.map
|
||||
originWidthMap = it.getParcelable<ParcelableMap<String, Int>>(KEY_WIDTH)?.map
|
||||
}
|
||||
|
||||
viewModel?.selectedPreviewDataListLiveData?.observe(viewLifecycleOwner) { list ->
|
||||
if (list.isNullOrEmpty()) {
|
||||
quickSelectRecyclerView?.visibility = View.GONE
|
||||
} else {
|
||||
quickSelectRecyclerView?.visibility = View.VISIBLE
|
||||
if (quickSelectRecyclerView?.adapter == null) {
|
||||
quickSelectRecyclerView?.layoutManager =
|
||||
LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
|
||||
quickSelectAdapter =
|
||||
QuickSelectAdapter(requireContext(), parentViewModel?.getAlbumId() ?: "", viewModel!!)
|
||||
quickSelectRecyclerView?.adapter = quickSelectAdapter
|
||||
}
|
||||
|
||||
quickSelectAdapter?.submitList(list)
|
||||
}
|
||||
|
||||
binding.mediaControl.previewTv.isEnabled = list.isNotEmpty()
|
||||
binding.mediaControl.confirmTv.isEnabled = list.isNotEmpty()
|
||||
if (list.isNotEmpty()) {
|
||||
binding.mediaControl.previewTv.setTextColor(R.color.text_instance.toColor(requireContext()))
|
||||
binding.mediaControl.confirmTv.text = "确定(${list.size})"
|
||||
binding.mediaControl.confirmTv.alpha = 1f
|
||||
} else {
|
||||
binding.mediaControl.previewTv.setTextColor(R.color.text_secondary.toColor(requireContext()))
|
||||
binding.mediaControl.confirmTv.text = "确定"
|
||||
binding.mediaControl.confirmTv.alpha = 0.6f
|
||||
}
|
||||
binding.mediaControl.confirmTv.setOnClickListener {
|
||||
parentViewModel?.postSelectedResult()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel?.positionLiveData?.observe(viewLifecycleOwner) {
|
||||
viewPager.setCurrentItem(it, false)
|
||||
}
|
||||
|
||||
binding.checkedIv.setOnClickListener { binding.checkedContainer.performClick() }
|
||||
binding.checkedContainer.enlargeTouchArea(10F.dip2px())
|
||||
binding.checkedContainer.setOnClickListener {
|
||||
if (currentDisplayingItem == null) return@setOnClickListener
|
||||
|
||||
if (parentViewModel?.isSelected(currentDisplayingItem!!) == true) {
|
||||
parentViewModel?.removeSelection(currentDisplayingItem!!)
|
||||
} else {
|
||||
if (parentViewModel?.addSelection(currentDisplayingItem!!) == false) {
|
||||
ToastUtils.showToast("至多选择${parentViewModel?.maxSelectionSize}张图片")
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectStatus()
|
||||
}
|
||||
|
||||
binding.mediaControl.previewTv.visibility = View.GONE
|
||||
|
||||
setAdapterAndPerformEnterAnimation()
|
||||
|
||||
if (album != null) {
|
||||
initEnterAnimation()
|
||||
}
|
||||
|
||||
DisplayUtils.transparentStatusAndNavigation(requireActivity())
|
||||
}
|
||||
|
||||
private fun setAdapterAndPerformEnterAnimation() {
|
||||
viewPagerAdapter = ViewImageAdapter()
|
||||
viewPager.addOnPageChangeListener(this)
|
||||
viewPager.adapter = viewPagerAdapter
|
||||
viewPager.currentItem = positionInAlbum
|
||||
|
||||
if (positionInAlbum == 0) {
|
||||
onPageSelected(0)
|
||||
}
|
||||
|
||||
currentDisplayingItem = parentViewModel?.getMediaItem(positionInAlbum, isSelectedOnly())
|
||||
}
|
||||
|
||||
override fun onHandleBackPressed(): Boolean {
|
||||
updateOriginPosition(currentDisplayingItem?.uri?.toString() ?: "", viewPager.currentItem)
|
||||
finishWithAnimation()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt(EntranceConsts.KEY_CURRENTITEM, viewPager.currentItem)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新动画还原时的位置
|
||||
*
|
||||
* @param url 图片地址
|
||||
* @param position 当前位置
|
||||
*/
|
||||
private fun updateOriginPosition(url: String, position: Int) {
|
||||
val left = originLeftMap?.get(url)
|
||||
val top = originTopMap?.get(url)
|
||||
val height = originHeightMap?.get(url)
|
||||
val width = originWidthMap?.get(url)
|
||||
|
||||
if (left == null || top == null || height == null || width == null) {
|
||||
return
|
||||
}
|
||||
|
||||
container = containerMap[position]
|
||||
if (container != null) {
|
||||
bigImageView = containerMap[position].findViewById(R.id.iv)
|
||||
}
|
||||
originLeft = left
|
||||
originTop = top
|
||||
originHeight = height
|
||||
originWidth = width
|
||||
resizeImage(url)
|
||||
originCenterX = originLeft + originWidth / 2
|
||||
originCenterY = originTop + originHeight / 2
|
||||
originHeightWidthRatio = originHeight.toFloat() / originWidth
|
||||
scaleX = originWidth.toFloat() / targetWidth
|
||||
scaleY = scaleX
|
||||
translationX = originCenterX - targetCenterX
|
||||
translationY = originCenterY - targetCenterY
|
||||
}
|
||||
|
||||
// 处理非等比例缩放的图片
|
||||
private fun resizeImage(url: String) {
|
||||
bigImageView?.ssiv?.run {
|
||||
imageRatio = sWidth / sHeight.toFloat()
|
||||
viewRatio = originWidth / originHeight.toFloat()
|
||||
if (!shouldResize()) return
|
||||
if (viewRatio < imageRatio) {
|
||||
originWidth = (originHeight * imageRatio).round2Int()
|
||||
originLeft -= (originWidth - (originWidthMap?.get(url) ?: 0)) / 2
|
||||
} else {
|
||||
originHeight = (originWidth / imageRatio).round2Int()
|
||||
if (justChangeBoundsAndTransform()) return
|
||||
originTop -= (originHeight - (originHeightMap?.get(url) ?: 0)) / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Float.round2Int() = if (!isNaN()) roundToInt() else 0
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
currentDisplayingItem = parentViewModel?.getMediaItem(position, isSelectedOnly())
|
||||
|
||||
currentDisplayingItem?.let {
|
||||
viewModel?.highlightItem(it)
|
||||
|
||||
parentViewModel?.indexOfSelected(it)?.let { quickPosition ->
|
||||
if (quickPosition == -1) return@let
|
||||
|
||||
quickSelectRecyclerView?.doOnLayout {
|
||||
quickSelectRecyclerView?.smoothScrollToPosition(quickPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectStatus()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onPageScrolled(
|
||||
position: Int, positionOffset: Float,
|
||||
positionOffsetPixels: Int
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(newState: Int) {}
|
||||
|
||||
private fun isSelectedOnly(): Boolean = album == null
|
||||
|
||||
private fun updateSelectStatus() {
|
||||
currentDisplayingItem?.let {
|
||||
if (parentViewModel?.isSelected(it) == true) {
|
||||
if (parentViewModel?.maxSelectionSize != 1) {
|
||||
binding.checkedIv.setImageResource(R.drawable.ic_choose_media_bg)
|
||||
binding.chooseCountTv.visibility = View.VISIBLE
|
||||
binding.chooseCountTv.text = "${(parentViewModel?.indexOfSelected(it) ?: 0) + 1}"
|
||||
} else {
|
||||
binding.chooseCountTv.visibility = View.GONE
|
||||
binding.checkedIv.setImageResource(R.drawable.ic_toolbar_checked)
|
||||
}
|
||||
} else {
|
||||
binding.chooseCountTv.visibility = View.GONE
|
||||
binding.checkedIv.setImageResource(R.drawable.ic_toolbar_check)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initEnterAnimation() {
|
||||
viewPager.viewTreeObserver
|
||||
.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
viewPager.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
|
||||
originLeft = originLeftMap?.get(previewItem?.uri?.toString()) ?: 0
|
||||
originTop = originTopMap?.get(previewItem?.uri?.toString()) ?: 0
|
||||
originHeight = originHeightMap?.get(previewItem?.uri?.toString()) ?: 0
|
||||
originWidth = originWidthMap?.get(previewItem?.uri?.toString()) ?: 0
|
||||
originCenterX = originLeft + originWidth / 2
|
||||
originCenterY = originTop + originHeight / 2
|
||||
originHeightWidthRatio = originHeight.toFloat() / originWidth
|
||||
val location = IntArray(2)
|
||||
bigImageView?.getLocationOnScreen(location)
|
||||
targetHeight = bigImageView?.height?.toFloat() ?: 0F
|
||||
targetWidth = bigImageView?.width?.toFloat() ?: 0F
|
||||
scaleX =
|
||||
if (originWidth == 0 && targetWidth == 0F) 1F else originWidth.toFloat() / targetWidth
|
||||
scaleY = scaleX
|
||||
targetCenterX = location[0] + targetWidth / 2
|
||||
targetCenterY = location[1] + targetHeight / 2
|
||||
translationX = originCenterX - targetCenterX
|
||||
translationY = originCenterY - targetCenterY
|
||||
|
||||
if (useEnterAndExitAnimation) {
|
||||
bigImageView?.translationX = translationX
|
||||
bigImageView?.translationY = translationY
|
||||
if (!scaleX.isNaN() && scaleX < Float.MAX_VALUE) {
|
||||
bigImageView?.scaleX = scaleX
|
||||
bigImageView?.scaleY = scaleY
|
||||
}
|
||||
}
|
||||
|
||||
performEnterAnimation()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadImage(
|
||||
uri: Uri?,
|
||||
imageView: BigImageView
|
||||
) {
|
||||
// 添加GIF支持
|
||||
imageView.setImageViewFactory(FrescoImageViewFactory())
|
||||
imageView.setThumbnailScaleType(ImageView.ScaleType.FIT_CENTER)
|
||||
imageView.showImage(uri)
|
||||
}
|
||||
|
||||
private fun finishWithAnimation() {
|
||||
val fadeOnly = isFadeOnly()
|
||||
|
||||
val animatorSet = AnimatorSet()
|
||||
|
||||
val translateXAnimator = ValueAnimator.ofFloat(0F, translationX).apply {
|
||||
addUpdateListener { va -> bigImageView?.x = (va.animatedValue as Float) }
|
||||
}
|
||||
val translateYAnimator = ValueAnimator.ofFloat(0F, translationY).apply {
|
||||
addUpdateListener { va -> bigImageView?.y = (va.animatedValue as Float) }
|
||||
}
|
||||
val scaleYAnimator = ValueAnimator.ofFloat(1F, scaleY).apply {
|
||||
addUpdateListener { va ->
|
||||
if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
|
||||
bigImageView?.scaleY = (va.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
val scaleXAnimator = ValueAnimator.ofFloat(1F, scaleX).apply {
|
||||
addUpdateListener { va ->
|
||||
if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
|
||||
bigImageView?.scaleX = (va.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
val backgroundAlphaAnimation = ValueAnimator.ofFloat(1F, 0F).apply {
|
||||
addUpdateListener { va -> backgroundView.alpha = (va.animatedValue as Float) }
|
||||
}
|
||||
val alphaAnimator = ValueAnimator.ofFloat(1F, 0F).apply {
|
||||
addUpdateListener { va -> viewPager.alpha = (va.animatedValue as Float) }
|
||||
}
|
||||
|
||||
if (useEnterAndExitAnimation && !fadeOnly) {
|
||||
if (currentDisplayingItem?.isGif == true) {
|
||||
animatorSet.apply {
|
||||
playTogether(
|
||||
translateXAnimator,
|
||||
translateYAnimator,
|
||||
scaleXAnimator,
|
||||
scaleYAnimator,
|
||||
backgroundAlphaAnimation
|
||||
)
|
||||
duration = ANIMATION_DURATION
|
||||
doOnStart {
|
||||
bottomContainer.visibility = View.GONE
|
||||
toolbarContainer.visibility = View.GONE
|
||||
}
|
||||
doOnEnd {
|
||||
it.removeAllListeners()
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
}.start()
|
||||
} else {
|
||||
startEndTransition()
|
||||
}
|
||||
} else {
|
||||
animatorSet.apply {
|
||||
playTogether(alphaAnimator, backgroundAlphaAnimation)
|
||||
duration = ANIMATION_DURATION
|
||||
doOnStart {
|
||||
bottomContainer.visibility = View.GONE
|
||||
toolbarContainer.visibility = View.GONE
|
||||
}
|
||||
doOnEnd {
|
||||
it.removeAllListeners()
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldResize() = abs(imageRatio - viewRatio) > RATIO_DIFF
|
||||
|
||||
private fun justChangeBoundsAndTransform() =
|
||||
imageRatio < viewRatio && viewPagerAdapter?.count == 1
|
||||
|
||||
private fun startEndTransition() {
|
||||
if (animating) return
|
||||
val doResizeTransition = Runnable {
|
||||
TransitionManager.beginDelayedTransition(
|
||||
container as ViewGroup,
|
||||
TransitionSet().apply {
|
||||
addTransition(ChangeBounds())
|
||||
addTransition(ChangeTransform())
|
||||
if (!justChangeBoundsAndTransform()) addTransition(ResizeTransition())
|
||||
duration = RESIZE_DURATION
|
||||
interpolator = DecelerateInterpolator()
|
||||
addListener(object : TransitionListenerAdapter() {
|
||||
override fun onTransitionStart(transition: Transition) {
|
||||
animating = true
|
||||
}
|
||||
|
||||
override fun onTransitionEnd(transition: Transition) {
|
||||
if (!animating) return
|
||||
animating = false
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
})
|
||||
})
|
||||
bigImageView?.ssiv?.run {
|
||||
layoutParams = layoutParams.apply {
|
||||
if (viewRatio < imageRatio) {
|
||||
width = originWidthMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
|
||||
} else {
|
||||
height = originHeightMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
|
||||
}
|
||||
if (this is ViewGroup.MarginLayoutParams) {
|
||||
if (viewRatio < imageRatio) {
|
||||
leftMargin =
|
||||
originLeftMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
|
||||
} else {
|
||||
topMargin =
|
||||
originTopMap?.get(currentDisplayingItem?.uri?.toString()) ?: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val doTransition = Runnable {
|
||||
TransitionManager.beginDelayedTransition(
|
||||
container as ViewGroup,
|
||||
TransitionSet().apply {
|
||||
addTransition(ChangeBounds())
|
||||
addTransition(ChangeTransform())
|
||||
addTransition(SsivTransition())
|
||||
duration = ANIMATION_DURATION
|
||||
interpolator = DecelerateInterpolator()
|
||||
addListener(object : TransitionListenerAdapter() {
|
||||
override fun onTransitionStart(transition: Transition) {
|
||||
animating = true
|
||||
bottomContainer.visibility = View.GONE
|
||||
toolbarContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onTransitionEnd(transition: Transition) {
|
||||
if (!animating) return
|
||||
animating = false
|
||||
if (shouldResize()) {
|
||||
container?.post(doResizeTransition)
|
||||
} else {
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
backgroundView.animate().setDuration(ANIMATION_DURATION).alpha(0F).start()
|
||||
bigImageView?.run {
|
||||
scaleX = 1F
|
||||
scaleY = 1F
|
||||
translationX = 0F
|
||||
translationY = 0F
|
||||
}
|
||||
bigImageView?.ssiv?.run {
|
||||
layoutParams = layoutParams.apply {
|
||||
width = originWidth
|
||||
height = originHeight
|
||||
if (this is ViewGroup.MarginLayoutParams) {
|
||||
leftMargin = originLeft
|
||||
topMargin = originTop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
container?.post(doTransition)
|
||||
lifecycle.addObserver(object : LifecycleEventObserver {
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
if (event == Lifecycle.Event.ON_DESTROY) {
|
||||
lifecycle.removeObserver(this)
|
||||
animating = false
|
||||
container?.removeCallbacks(doTransition)
|
||||
if (shouldResize()) container?.removeCallbacks(doResizeTransition)
|
||||
TransitionManager.endTransitions(container as ViewGroup)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun performEnterAnimation() {
|
||||
val animatorSet = AnimatorSet()
|
||||
|
||||
val translateXAnimator = ValueAnimator.ofFloat(bigImageView!!.x, 0F).apply {
|
||||
addUpdateListener { va -> bigImageView?.x = (va.animatedValue as Float) }
|
||||
}
|
||||
val translateYAnimator = ValueAnimator.ofFloat(bigImageView!!.y, 0F).apply {
|
||||
addUpdateListener { va -> bigImageView?.y = (va.animatedValue as Float) }
|
||||
}
|
||||
val scaleYAnimator = ValueAnimator.ofFloat(scaleY, 1F).apply {
|
||||
addUpdateListener { va ->
|
||||
if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
|
||||
bigImageView?.scaleY = (va.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
val scaleXAnimator = ValueAnimator.ofFloat(scaleX, 1F).apply {
|
||||
addUpdateListener { va ->
|
||||
if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
|
||||
bigImageView?.scaleX = (va.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
val backgroundAlphaAnimator = ValueAnimator.ofFloat(0F, 1F).apply {
|
||||
addUpdateListener { va -> backgroundView.alpha = (va.animatedValue as Float) }
|
||||
}
|
||||
|
||||
animatorSet.apply {
|
||||
if (useEnterAndExitAnimation) {
|
||||
playTogether(
|
||||
translateXAnimator,
|
||||
translateYAnimator,
|
||||
scaleXAnimator,
|
||||
scaleYAnimator,
|
||||
backgroundAlphaAnimator
|
||||
)
|
||||
} else {
|
||||
playTogether(backgroundAlphaAnimator)
|
||||
}
|
||||
duration = ANIMATION_DURATION
|
||||
doOnStart {
|
||||
bottomContainer.visibility = View.GONE
|
||||
toolbarContainer.visibility = View.GONE
|
||||
}
|
||||
doOnEnd {
|
||||
bottomContainer.visibility = View.VISIBLE
|
||||
toolbarContainer.visibility = View.VISIBLE
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun performExitAnimation(view: View?, scale: Float, fadeOnly: Boolean) {
|
||||
if (view == null) {
|
||||
parentFragmentManager.popBackStack()
|
||||
return
|
||||
}
|
||||
|
||||
val finalScale = originWidth / targetWidth
|
||||
val finalTranslationX = originLeft - (1 - finalScale) * targetWidth / 2
|
||||
val finalTranslationY =
|
||||
originTop - ((1 - finalScale) * targetHeight + (targetHeight * finalScale - originHeight)) / 2
|
||||
|
||||
val animatorSet = AnimatorSet()
|
||||
|
||||
val scaleAnimator = ValueAnimator.ofFloat(scale, finalScale).apply {
|
||||
addUpdateListener { va ->
|
||||
if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) {
|
||||
view.scaleX = (va.animatedValue as Float)
|
||||
view.scaleY = (va.animatedValue as Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
val translateXAnimator = ValueAnimator.ofFloat(view.x, finalTranslationX).apply {
|
||||
addUpdateListener { va -> view.translationX = (va.animatedValue as Float) }
|
||||
}
|
||||
val translateYAnimator = ValueAnimator.ofFloat(view.y, finalTranslationY).apply {
|
||||
addUpdateListener { va -> view.translationY = (va.animatedValue as Float) }
|
||||
}
|
||||
val backgroundAlphaAnimator = ValueAnimator.ofFloat(backgroundView.alpha, 0F).apply {
|
||||
addUpdateListener { va -> backgroundView.alpha = (va.animatedValue as Float) }
|
||||
}
|
||||
val alphaAnimator = ValueAnimator.ofFloat(1F, 0F).apply {
|
||||
addUpdateListener { va -> view.alpha = (va.animatedValue as Float) }
|
||||
}
|
||||
|
||||
if (useEnterAndExitAnimation && !fadeOnly) {
|
||||
if (currentDisplayingItem?.isGif == true) {
|
||||
animatorSet.apply {
|
||||
playTogether(
|
||||
scaleAnimator,
|
||||
translateXAnimator,
|
||||
translateYAnimator,
|
||||
backgroundAlphaAnimator
|
||||
)
|
||||
doOnEnd {
|
||||
it.removeAllListeners()
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
interpolator = DecelerateInterpolator()
|
||||
duration = ANIMATION_DURATION
|
||||
}.start()
|
||||
} else {
|
||||
startEndTransition()
|
||||
}
|
||||
} else {
|
||||
animatorSet.apply {
|
||||
playTogether(backgroundAlphaAnimator, alphaAnimator)
|
||||
doOnEnd {
|
||||
it.removeAllListeners()
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
interpolator = DecelerateInterpolator()
|
||||
duration = ANIMATION_DURATION
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ResizeTransition : Transition() {
|
||||
override fun captureStartValues(transitionValues: TransitionValues) {
|
||||
captureValues(transitionValues)
|
||||
}
|
||||
|
||||
override fun captureEndValues(transitionValues: TransitionValues) {
|
||||
captureValues(transitionValues)
|
||||
}
|
||||
|
||||
private fun captureValues(transitionValues: TransitionValues) {
|
||||
transitionValues.values["width"] = transitionValues.view.width
|
||||
transitionValues.values["height"] = transitionValues.view.height
|
||||
}
|
||||
|
||||
override fun createAnimator(
|
||||
sceneRoot: ViewGroup,
|
||||
startValues: TransitionValues?,
|
||||
endValues: TransitionValues?
|
||||
): Animator? {
|
||||
val startWidth = (startValues?.values?.get("width") as Int?) ?: 0
|
||||
val endWidth = (endValues?.values?.get("width") as Int?) ?: 0
|
||||
val startHeight = (startValues?.values?.get("height") as Int?) ?: 0
|
||||
val endHeight = (endValues?.values?.get("height") as Int?) ?: 0
|
||||
|
||||
val firstValue =
|
||||
if (viewRatio < imageRatio) startWidth.toFloat() else startHeight.toFloat()
|
||||
val secondValue =
|
||||
if (viewRatio < imageRatio) endWidth.toFloat() else endHeight.toFloat()
|
||||
return ObjectAnimator.ofFloat(firstValue, secondValue).apply {
|
||||
addUpdateListener {
|
||||
sceneRoot.findViewById<BigImageView>(R.id.iv)?.ssiv?.run {
|
||||
maxScale = this.scale
|
||||
setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP)
|
||||
resetScaleAndCenter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class SsivTransition : Transition() {
|
||||
override fun captureStartValues(transitionValues: TransitionValues) {
|
||||
captureValues(transitionValues)
|
||||
}
|
||||
|
||||
override fun captureEndValues(transitionValues: TransitionValues) {
|
||||
captureValues(transitionValues)
|
||||
}
|
||||
|
||||
private fun captureValues(transitionValues: TransitionValues) {
|
||||
transitionValues.values["width"] = transitionValues.view.width
|
||||
transitionValues.values["height"] = transitionValues.view.height
|
||||
}
|
||||
|
||||
override fun createAnimator(
|
||||
sceneRoot: ViewGroup,
|
||||
startValues: TransitionValues?,
|
||||
endValues: TransitionValues?
|
||||
): Animator? {
|
||||
val startHeight = (startValues?.values?.get("height") as Int?) ?: 0
|
||||
val endHeight = (endValues?.values?.get("height") as Int?) ?: 0
|
||||
return ObjectAnimator.ofFloat(startHeight.toFloat(), endHeight.toFloat()).apply {
|
||||
addUpdateListener {
|
||||
sceneRoot.findViewById<BigImageView>(R.id.iv)?.ssiv?.run {
|
||||
setScaleAndCenter(minScale, PointF(sWidth / 2F, sHeight / 2F))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ViewImageAdapter : PagerAdapter() {
|
||||
override fun getCount() = parentViewModel?.getMediaCount(isSelectedOnly()) ?: 0
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val item = parentViewModel?.getMediaItem(position, isSelectedOnly())
|
||||
|
||||
val view = View.inflate(container.context, R.layout.local_media_preview_item, null) as ViewGroup
|
||||
val imageView: DraggableBigImageView = view.findViewById(R.id.iv)
|
||||
if (useEnterAndExitAnimation) {
|
||||
containerMap.put(position, view)
|
||||
}
|
||||
if (bigImageView == null) {
|
||||
bigImageView = imageView
|
||||
}
|
||||
imageView.setDragListener(object : DraggableBigImageView.DragListener {
|
||||
override fun onRelease(draggableBigImageView: DraggableBigImageView, scale: Float) {
|
||||
updateOriginPosition(currentDisplayingItem?.uri?.toString() ?: "", viewPager.currentItem)
|
||||
performExitAnimation(draggableBigImageView, scale, isFadeOnly())
|
||||
}
|
||||
|
||||
override fun onDrag(draggableBigImageView: DraggableBigImageView, fraction: Float) {
|
||||
backgroundView.alpha = 1 - fraction
|
||||
bottomContainer.visibility = View.GONE
|
||||
toolbarContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onRestore(
|
||||
draggableBigImageView: DraggableBigImageView,
|
||||
fraction: Float
|
||||
) {
|
||||
backgroundView.alpha = 1F
|
||||
bottomContainer.visibility = View.VISIBLE
|
||||
toolbarContainer.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
|
||||
imageView.setOnClickListener {
|
||||
if (bottomContainer.visibility == View.VISIBLE) {
|
||||
bottomContainer.visibility = View.GONE
|
||||
toolbarContainer.visibility = View.GONE
|
||||
} else {
|
||||
bottomContainer.visibility = View.VISIBLE
|
||||
toolbarContainer.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
imageView.setImageLoaderCallback(object : SimpleImageLoader() {
|
||||
override fun onSuccess(image: File) {
|
||||
val ssiv = imageView.ssiv
|
||||
if (ssiv != null) {
|
||||
ssiv.maxScale = 10f // 这个缩放倍数最好很具宽高自动调节
|
||||
}
|
||||
}
|
||||
})
|
||||
loadImage(item?.uri, imageView)
|
||||
|
||||
view.tag = position
|
||||
container.addView(view)
|
||||
return view
|
||||
}
|
||||
|
||||
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||
container.removeView(`object` as View)
|
||||
}
|
||||
|
||||
override fun isViewFromObject(view: View, `object`: Any): Boolean {
|
||||
return view === `object`
|
||||
}
|
||||
}
|
||||
|
||||
private fun isFadeOnly() = originLeft == 0 || originTop == 0
|
||||
|
||||
companion object {
|
||||
const val RATIO_DIFF = 0.01F
|
||||
|
||||
private const val KEY_USE_ENTER_AND_EXIT_ANIMATION = "use_enter_and_exit_animation"
|
||||
|
||||
const val KEY_ITEM_LIST = "item_list"
|
||||
const val KEY_ALBUM = "album"
|
||||
const val KEY_POSITION_IN_ALBUM = "position_in_album"
|
||||
const val KEY_PREVIEW_ITEM = "preview_item"
|
||||
|
||||
private const val KEY_LEFT = "left"
|
||||
private const val KEY_TOP = "top"
|
||||
private const val KEY_HEIGHT = "height"
|
||||
private const val KEY_WIDTH = "width"
|
||||
|
||||
private const val ANIMATION_DURATION = 350L
|
||||
private const val RESIZE_DURATION = 100L
|
||||
|
||||
/**
|
||||
* 传入 viewList 代表使用渐入渐出动画
|
||||
*/
|
||||
fun getInstance(
|
||||
selectedItemList: ArrayList<Item>,
|
||||
album: Album?,
|
||||
positionInAlbum: Int = 0,
|
||||
previewItem: Item? = null,
|
||||
originalUrlAndViewMap: Map<String, View>? = null,
|
||||
): LocalMediaPreviewFragment {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelableArrayList(KEY_ITEM_LIST, selectedItemList)
|
||||
bundle.putParcelable(KEY_ALBUM, album)
|
||||
bundle.putInt(KEY_POSITION_IN_ALBUM, positionInAlbum)
|
||||
bundle.putParcelable(KEY_PREVIEW_ITEM, previewItem)
|
||||
|
||||
if (originalUrlAndViewMap != null) {
|
||||
val leftMap = hashMapOf<String, Int>()
|
||||
val topMap = hashMapOf<String, Int>()
|
||||
val heightMap = hashMapOf<String, Int>()
|
||||
val widthMap = hashMapOf<String, Int>()
|
||||
for (entry in originalUrlAndViewMap) {
|
||||
val location = IntArray(2)
|
||||
val url = entry.key
|
||||
val view = entry.value
|
||||
|
||||
view.getLocationOnScreen(location)
|
||||
leftMap.put(url, location[0])
|
||||
topMap.put(url, location[1])
|
||||
heightMap.put(url, view.height)
|
||||
widthMap.put(url, view.width)
|
||||
}
|
||||
bundle.putParcelable(KEY_LEFT, ParcelableMap(leftMap))
|
||||
bundle.putParcelable(KEY_TOP, ParcelableMap(topMap))
|
||||
bundle.putParcelable(KEY_HEIGHT, ParcelableMap(heightMap))
|
||||
bundle.putParcelable(KEY_WIDTH, ParcelableMap(widthMap))
|
||||
bundle.putBoolean(KEY_USE_ENTER_AND_EXIT_ANIMATION, true)
|
||||
}
|
||||
|
||||
return LocalMediaPreviewFragment().also { it.with(bundle) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LocalMediaPreviewViewModel(private val localMediaRepo: LocalMediaRepository) : ViewModel() {
|
||||
|
||||
private var highlightedItem: Item? = null
|
||||
private var _positionLiveData: MutableLiveData<Int> = MutableLiveData()
|
||||
val positionLiveData: LiveData<Int> = _positionLiveData
|
||||
|
||||
private val _selectedPreviewDataListLiveData by lazy { MutableLiveData<List<SelectedPreviewData>>() }
|
||||
val selectedPreviewDataListLiveData: LiveData<List<SelectedPreviewData>> = _selectedPreviewDataListLiveData
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
localMediaRepo.selectedItemListFlow.collect { itemList ->
|
||||
emitSelectedPreviewData(itemList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun scrollToPosition(position: Int) {
|
||||
_positionLiveData.value = position
|
||||
}
|
||||
|
||||
fun highlightItem(item: Item) {
|
||||
highlightedItem = item
|
||||
|
||||
emitSelectedPreviewData(localMediaRepo.selectedItemListFlow.value)
|
||||
}
|
||||
|
||||
fun emitSelectedPreviewData(itemList: List<Item>) {
|
||||
// Optionally modify each item
|
||||
val newSelectedPreviewDataList = arrayListOf<SelectedPreviewData>()
|
||||
for (item in itemList) {
|
||||
newSelectedPreviewDataList.add(
|
||||
SelectedPreviewData(item.contentUri == highlightedItem?.contentUri, item)
|
||||
)
|
||||
}
|
||||
|
||||
// Emit the updated list
|
||||
_selectedPreviewDataListLiveData.postValue(newSelectedPreviewDataList)
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val localMediaRepo: LocalMediaRepository
|
||||
) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return LocalMediaPreviewViewModel(localMediaRepo) as T
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class LocalMediaRepository {
|
||||
|
||||
private val _selectedItemListFlow = MutableStateFlow<ArrayList<Item>>(arrayListOf())
|
||||
val selectedItemListFlow: StateFlow<ArrayList<Item>> = _selectedItemListFlow
|
||||
|
||||
private val _selectedMediaList = ArrayList<Item>()
|
||||
val selectedMediaList
|
||||
get() = _selectedMediaList
|
||||
|
||||
suspend fun addSelection(item: Item) {
|
||||
_selectedMediaList.add(item)
|
||||
_selectedItemListFlow.emit(ArrayList(_selectedMediaList))
|
||||
}
|
||||
|
||||
suspend fun removeSelection(item: Item) {
|
||||
_selectedMediaList.remove(item)
|
||||
_selectedItemListFlow.emit(ArrayList(_selectedMediaList))
|
||||
}
|
||||
|
||||
fun isSelected(item: Item): Boolean {
|
||||
return _selectedMediaList.contains(item)
|
||||
}
|
||||
|
||||
fun indexOfSelected(item: Item): Int {
|
||||
return _selectedMediaList.indexOf(item)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.database.Cursor
|
||||
import android.security.identity.ResultData
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.zhihu.matisse.internal.entity.Album
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LocalMediaViewModel(val maxSelectionSize: Int, val localMediaRepo: LocalMediaRepository) : ViewModel() {
|
||||
|
||||
private val _previewImageLiveData = MutableLiveData<PreviewData>()
|
||||
val previewImageLiveData: LiveData<PreviewData> = _previewImageLiveData
|
||||
|
||||
private val _resultLiveData = MutableLiveData<Boolean>()
|
||||
val resultLiveData: LiveData<Boolean> = _resultLiveData
|
||||
|
||||
val selectedCountLiveData: LiveData<Int> = localMediaRepo.selectedItemListFlow.map { it.size }.asLiveData()
|
||||
val selectedMediaListLiveData: LiveData<ArrayList<Item>> = localMediaRepo.selectedItemListFlow.asLiveData()
|
||||
|
||||
private var albumCursor: Cursor? = null
|
||||
|
||||
val selectedMediaList
|
||||
get() = localMediaRepo.selectedMediaList
|
||||
|
||||
private var mediaAlbum: Album? = null
|
||||
|
||||
fun getAlbumId(): String? {
|
||||
return mediaAlbum?.id
|
||||
}
|
||||
|
||||
fun updateAlbum(album: Album) {
|
||||
mediaAlbum = album
|
||||
}
|
||||
|
||||
// TODO 记得在 onDestroy 时关闭 cursor
|
||||
fun updateAlbumCursor(cursor: Cursor) {
|
||||
albumCursor = cursor
|
||||
}
|
||||
|
||||
fun previewImage(
|
||||
onlySelected: Boolean,
|
||||
positionInAlbum: Int = 0,
|
||||
previewItem: Item? = null,
|
||||
urlAndViewMap: Map<String, android.view.View> = emptyMap()
|
||||
) {
|
||||
if (onlySelected) {
|
||||
_previewImageLiveData.value = PreviewData(localMediaRepo.selectedMediaList, null, 0, previewItem, urlAndViewMap)
|
||||
} else {
|
||||
_previewImageLiveData.value =
|
||||
PreviewData(localMediaRepo.selectedMediaList, mediaAlbum, positionInAlbum, previewItem, urlAndViewMap)
|
||||
}
|
||||
}
|
||||
|
||||
fun addSelection(item: Item): Boolean {
|
||||
if (localMediaRepo.selectedMediaList.size >= maxSelectionSize) {
|
||||
return false
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
localMediaRepo.addSelection(item)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun removeSelection(item: Item) {
|
||||
viewModelScope.launch {
|
||||
localMediaRepo.removeSelection(item)
|
||||
}
|
||||
}
|
||||
|
||||
fun isSelected(item: Item): Boolean {
|
||||
return localMediaRepo.selectedMediaList.contains(item)
|
||||
}
|
||||
|
||||
fun indexOfSelected(item: Item): Int {
|
||||
return localMediaRepo.selectedMediaList.indexOf(item)
|
||||
}
|
||||
|
||||
fun getMediaCount(selectedOnly: Boolean): Int {
|
||||
return if (selectedOnly) {
|
||||
selectedMediaList.size
|
||||
} else {
|
||||
albumCursor?.count ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
fun getMediaItem(position: Int, selectedOnly: Boolean): Item {
|
||||
return if (selectedOnly) {
|
||||
selectedMediaList[position]
|
||||
} else {
|
||||
albumCursor?.moveToPosition(position)
|
||||
Item.valueOf(albumCursor).also {
|
||||
it.positionInAlbum = position
|
||||
it.albumId = getAlbumId()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun postSelectedResult() {
|
||||
_resultLiveData.postValue(true)
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val maxSelectionSize: Int,
|
||||
private val localMediaRepo: LocalMediaRepository
|
||||
) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return LocalMediaViewModel(maxSelectionSize, localMediaRepo) as T
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.view.View
|
||||
import com.zhihu.matisse.internal.entity.Album
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
|
||||
class PreviewData(
|
||||
val previewList: ArrayList<Item>,
|
||||
val album: Album?,
|
||||
val positionInAlbum: Int,
|
||||
val previewItem: Item?,
|
||||
val urlAndViewMap: Map<String, View>
|
||||
)
|
||||
@ -0,0 +1,68 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.baselist.DiffUtilAdapter
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.feature.databinding.ItemLocalMediaPreviewQuickSelectBinding
|
||||
|
||||
internal class QuickSelectAdapter(
|
||||
context: Context,
|
||||
private val currentAlbumId: String,
|
||||
private val viewModel: LocalMediaPreviewViewModel
|
||||
) : DiffUtilAdapter<SelectedPreviewData>(context) {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): RecyclerView.ViewHolder {
|
||||
return ItemViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int
|
||||
) {
|
||||
val item = mDataList[position]
|
||||
|
||||
if (holder is ItemViewHolder) {
|
||||
holder.bindView(item)
|
||||
holder.binding.root.setOnClickListener {
|
||||
// 跨 album 的图片不允许快速跳转
|
||||
if (currentAlbumId != item.item.albumId) return@setOnClickListener
|
||||
|
||||
val targetPosition = if (item.item.positionInAlbum != -1) {
|
||||
item.item.positionInAlbum
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
viewModel.scrollToPosition(targetPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = mDataList.size
|
||||
|
||||
override fun areItemsTheSame(oldItem: SelectedPreviewData?, newItem: SelectedPreviewData?): Boolean {
|
||||
return oldItem?.item?.contentUri == newItem?.item?.contentUri
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: SelectedPreviewData?, newItem: SelectedPreviewData?): Boolean {
|
||||
return oldItem?.isHighlighted == newItem?.isHighlighted
|
||||
&& oldItem?.item?.contentUri == newItem?.item?.contentUri
|
||||
}
|
||||
|
||||
private class ItemViewHolder(var binding: ItemLocalMediaPreviewQuickSelectBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bindView(item: SelectedPreviewData) {
|
||||
ImageUtils.displayResizeMedia(binding.thumbnailIv, item.item.contentUri, 56, 56)
|
||||
|
||||
binding.selectedRingView.goneIf(!item.isHighlighted)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Item
|
||||
|
||||
class SelectedPreviewData(val isHighlighted: Boolean, val item: Item)
|
||||
@ -1,4 +1,4 @@
|
||||
package com.gh.gamecenter.qa.editor
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
@ -7,12 +7,17 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CursorAdapter
|
||||
import android.widget.TextView
|
||||
|
||||
import com.gh.gamecenter.common.R
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.zhihu.matisse.internal.entity.Album
|
||||
|
||||
class VideoAlbumsAdapter(context: Context) : CursorAdapter(context, null) {
|
||||
|
||||
private var selectedPosition = 0
|
||||
|
||||
override fun newView(context: Context?, cursor: Cursor?, parent: ViewGroup?): View {
|
||||
val inflate = LayoutInflater.from(context)
|
||||
return inflate.inflate(R.layout.video_albums_item, parent, false)
|
||||
@ -20,8 +25,14 @@ class VideoAlbumsAdapter(context: Context) : CursorAdapter(context, null) {
|
||||
|
||||
override fun bindView(view: View, context: Context, cursor: Cursor?) {
|
||||
val album = Album.valueOf(cursor)
|
||||
|
||||
view.findViewById<View>(R.id.checkIv).goneIf(cursor?.position != selectedPosition)
|
||||
view.findViewById<TextView>(R.id.album_name).text = album.getDisplayName(context)
|
||||
view.findViewById<TextView>(R.id.album_media_count).text = album.count.toString()
|
||||
view.findViewById<SimpleDraweeView>(R.id.album_cover).setImageURI(album.coverUri)
|
||||
}
|
||||
|
||||
fun updateSelectedPosition(position: Int) {
|
||||
selectedPosition = position
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.gh.gamecenter.qa.editor
|
||||
package com.gh.gamecenter.feature.selector
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
@ -11,11 +11,13 @@ import android.widget.PopupWindow
|
||||
import androidx.appcompat.widget.ListPopupWindow
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
import com.gh.gamecenter.common.R
|
||||
|
||||
class VideoAlbumsSpanner(val context: Context) {
|
||||
|
||||
private var mListPopupWindow: ListPopupWindow = CustomListPopupWindow(context, R.style.PosterListPopupWindow)
|
||||
private var mListPopupWindow: ListPopupWindow =
|
||||
CustomListPopupWindow(context, R.style.PosterListPopupWindow)
|
||||
|
||||
private lateinit var mAdapter: CursorAdapter
|
||||
|
||||
@ -28,7 +30,14 @@ class VideoAlbumsSpanner(val context: Context) {
|
||||
mListPopupWindow.dismiss()
|
||||
onItemSelectedListener?.onItemSelected(parent, view, position, id)
|
||||
}
|
||||
mListPopupWindow.setBackgroundDrawable(ColorDrawable(ContextCompat.getColor(context, R.color.transparent)))
|
||||
mListPopupWindow.setBackgroundDrawable(
|
||||
ColorDrawable(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.transparent
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun setDismissListener(listener: PopupWindow.OnDismissListener) {
|
||||
@ -52,8 +61,8 @@ class VideoAlbumsSpanner(val context: Context) {
|
||||
mListPopupWindow.show()
|
||||
val containerView = mListPopupWindow.listView as? ViewGroup
|
||||
val params = containerView?.layoutParams as ViewGroup.LayoutParams
|
||||
params.height = 280f.dip2px()
|
||||
containerView.background = ColorDrawable(ContextCompat.getColor(context, R.color.ui_surface))
|
||||
params.height = 280F.dip2px()
|
||||
containerView.background = ColorDrawable(ContextCompat.getColor(context, R.color.ui_surface_fixed_dark))
|
||||
containerView.layoutParams = params
|
||||
val parentContainer = containerView.parent as FrameLayout
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,35 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- First Stroke Layer -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/black" /> <!-- Second stroke color -->
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- Second Stroke Layer -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/primary_theme" /> <!-- First stroke color -->
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- Transparent Content Layer -->
|
||||
<item
|
||||
android:bottom="4dp"
|
||||
android:left="4dp"
|
||||
android:right="4dp"
|
||||
android:top="4dp">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="2dp" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
140
module_core_feature/src/main/res/layout/fragment_local_media.xml
Normal file
@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.gh.gamecenter.common.view.MaterializedConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_background_fixed_dark"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:id="@+id/statusBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/ui_surface_fixed_dark"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/normal_toolbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/appbar_height"
|
||||
app:layout_constraintTop_toBottomOf="@id/statusBar">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/normal_toolbar"
|
||||
style="@style/Base_ToolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_surface_fixed_dark"
|
||||
app:contentInsetEnd="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:navigationIcon="@null">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/backContainer"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backBtn"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:srcCompat="@drawable/ic_function_close" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/normal_title"
|
||||
style="@style/toolbar_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_marginRight="50dp"
|
||||
android:textColor="@color/text_aw_primary"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrowIv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginLeft="-42dp"
|
||||
android:layout_toRightOf="@+id/normal_title"
|
||||
app:srcCompat="@drawable/ic_pin_down" />
|
||||
</RelativeLayout>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/ui_background_fixed_dark"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/normal_toolbar_container">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/list_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/pieceMediaControl"
|
||||
layout="@layout/piece_media_control"/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_ll_loading"
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_no_connection"
|
||||
layout="@layout/reuse_no_connection" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_none_data"
|
||||
layout="@layout/reuse_none_data" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_data_exception"
|
||||
layout="@layout/reuse_data_exception" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_activity_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</com.gh.gamecenter.common.view.MaterializedConstraintLayout>
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/background_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black" />
|
||||
|
||||
<com.lightgame.view.NoScrollableViewPager
|
||||
android:id="@+id/image_detail_page"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/toolbarContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:id="@+id/statusBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/ui_surface_fixed_dark" />
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
style="@style/Base_ToolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_surface_fixed_dark"
|
||||
app:contentInsetEnd="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:navigationIcon="@null">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/backContainer"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backBtn"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:srcCompat="@drawable/ic_bar_back_light" />
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/checkedContainer"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkedIv"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_toolbar_check" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/chooseCountTv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@color/text_aw_primary"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/checkedIv"
|
||||
app:layout_constraintEnd_toEndOf="@+id/checkedIv"
|
||||
app:layout_constraintStart_toStartOf="@+id/checkedIv"
|
||||
app:layout_constraintTop_toTopOf="@+id/checkedIv" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottomContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/quickSelectRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="84dp"
|
||||
android:background="@color/ui_surface_fixed_dark"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
android:id="@+id/mediaControl"
|
||||
layout="@layout/piece_media_control" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/thumbnailIv"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_margin="4dp"
|
||||
app:roundedCornerRadius="4dp" />
|
||||
|
||||
<View
|
||||
android:id="@+id/selectedRingView"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_margin="4dp"
|
||||
android:background="@drawable/bg_local_media_preview_quick_selected" />
|
||||
|
||||
</FrameLayout>
|
||||
@ -17,7 +17,7 @@
|
||||
android:id="@+id/closeIv"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@drawable/ic_close"
|
||||
android:src="@drawable/ic_function_close"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/imageIv"
|
||||
app:layout_constraintTop_toTopOf="@id/imageIv" />
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.gh.gamecenter.common.view.Gh_RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.gh.gamecenter.common.view.DraggableBigImageView
|
||||
android:id="@+id/iv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:initScaleType="fitCenter"
|
||||
app:optimizeDisplay="true" />
|
||||
|
||||
<include
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
</com.gh.gamecenter.common.view.Gh_RelativeLayout>
|
||||
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:background="@color/ui_surface_fixed_dark">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/previewTv"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:enabled="false"
|
||||
android:gravity="center"
|
||||
android:text="预览"
|
||||
android:textColor="@color/text_instance"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/confirmTv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="16dp"
|
||||
android:alpha="0.6"
|
||||
android:background="@drawable/button_blue_oval"
|
||||
android:enabled="false"
|
||||
android:gravity="center"
|
||||
android:text="确定"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/ui_divider_fixed_dark"/>
|
||||
|
||||
</RelativeLayout>
|
||||