fix(이미지 선택): 이미지 선택 및 크롭 로직 수정
This commit is contained in:
@@ -148,7 +148,6 @@ dependencies {
|
|||||||
// permission
|
// permission
|
||||||
implementation "io.github.ParkSangGwon:tedpermission-normal:3.3.0"
|
implementation "io.github.ParkSangGwon:tedpermission-normal:3.3.0"
|
||||||
|
|
||||||
implementation 'com.github.dhaval2404:imagepicker:2.1'
|
|
||||||
implementation 'com.github.yalantis:ucrop:2.2.11'
|
implementation 'com.github.yalantis:ucrop:2.2.11'
|
||||||
implementation 'com.github.zhpanvip:bannerviewpager:3.5.7'
|
implementation 'com.github.zhpanvip:bannerviewpager:3.5.7'
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,18 @@ package kr.co.vividnext.sodalive.audio_content.modify
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.setPadding
|
import androidx.core.view.setPadding
|
||||||
import coil.load
|
import coil.load
|
||||||
import coil.transform.RoundedCornersTransformation
|
import coil.transform.RoundedCornersTransformation
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.gun0912.tedpermission.PermissionListener
|
import com.gun0912.tedpermission.PermissionListener
|
||||||
import com.gun0912.tedpermission.normal.TedPermission
|
import com.gun0912.tedpermission.normal.TedPermission
|
||||||
import com.jakewharton.rxbinding4.widget.textChanges
|
import com.jakewharton.rxbinding4.widget.textChanges
|
||||||
@@ -20,6 +22,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
|||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
import kr.co.vividnext.sodalive.common.Constants
|
import kr.co.vividnext.sodalive.common.Constants
|
||||||
|
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
import kr.co.vividnext.sodalive.common.RealPathUtil
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentModifyBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentModifyBinding
|
||||||
@@ -33,36 +36,7 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
|
|||||||
private val viewModel: AudioContentModifyViewModel by inject()
|
private val viewModel: AudioContentModifyViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
private lateinit var cropper: ImagePickerCropper
|
||||||
private val imageResult = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
val fileUri = data?.data
|
|
||||||
|
|
||||||
if (fileUri != null) {
|
|
||||||
binding.ivCover.setPadding(0)
|
|
||||||
binding.ivCover.background = null
|
|
||||||
binding.ivCover.load(fileUri) {
|
|
||||||
crossfade(true)
|
|
||||||
placeholder(R.drawable.bg_placeholder)
|
|
||||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
|
||||||
}
|
|
||||||
viewModel.coverImageUri = fileUri
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
"잘못된 파일입니다.\n다시 선택해 주세요.",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -82,24 +56,50 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
|
|||||||
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
|
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
cropper.cleanup()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
override fun setupView() {
|
override fun setupView() {
|
||||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
|
||||||
|
cropper = ImagePickerCropper(
|
||||||
|
caller = this,
|
||||||
|
context = this,
|
||||||
|
excludeGif = true,
|
||||||
|
isEnabledFreeStyleCrop = true,
|
||||||
|
config = ImagePickerCropper.Config(
|
||||||
|
aspectX = 1f, aspectY = 1f,
|
||||||
|
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
compressQuality = 90
|
||||||
|
),
|
||||||
|
onSuccess = { file, uri ->
|
||||||
|
binding.ivCover.setPadding(0)
|
||||||
|
binding.ivCover.background = null
|
||||||
|
Glide.with(this)
|
||||||
|
.load(uri)
|
||||||
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
|
.apply(
|
||||||
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
13.3f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(binding.ivCover)
|
||||||
|
|
||||||
|
viewModel.coverImageFile = file
|
||||||
|
},
|
||||||
|
onError = { e ->
|
||||||
|
Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
binding.toolbar.tvBack.text = "콘텐츠 수정"
|
binding.toolbar.tvBack.text = "콘텐츠 수정"
|
||||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||||
|
|
||||||
binding.ivPhotoPicker.setOnClickListener {
|
binding.ivPhotoPicker.setOnClickListener { cropper.launch() }
|
||||||
ImagePicker.with(this)
|
|
||||||
.crop()
|
|
||||||
.galleryOnly()
|
|
||||||
.galleryMimeTypes( // Exclude gif images
|
|
||||||
mimeTypes = arrayOf(
|
|
||||||
"image/png",
|
|
||||||
"image/jpg",
|
|
||||||
"image/jpeg"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.createIntent { imageResult.launch(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.llAvailablePoint.setOnClickListener { viewModel.setAvailablePoint(true) }
|
binding.llAvailablePoint.setOnClickListener { viewModel.setAvailablePoint(true) }
|
||||||
binding.llNotAvailablePoint.setOnClickListener { viewModel.setAvailablePoint(false) }
|
binding.llNotAvailablePoint.setOnClickListener { viewModel.setAvailablePoint(false) }
|
||||||
@@ -239,8 +239,8 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
|
|||||||
viewModel.setAdult(true)
|
viewModel.setAdult(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.isAdultLiveData.observe(this) {
|
viewModel.isAdultLiveData.observe(this) { isAdult ->
|
||||||
if (it) {
|
if (isAdult) {
|
||||||
binding.ivAgeAll.visibility = View.GONE
|
binding.ivAgeAll.visibility = View.GONE
|
||||||
binding.llAgeAll.setBackgroundResource(
|
binding.llAgeAll.setBackgroundResource(
|
||||||
R.drawable.bg_round_corner_6_7_13181b
|
R.drawable.bg_round_corner_6_7_13181b
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class AudioContentModifyViewModel(
|
|||||||
var title: String? = null
|
var title: String? = null
|
||||||
var detail: String? = null
|
var detail: String? = null
|
||||||
var tags: String? = null
|
var tags: String? = null
|
||||||
var coverImageUri: Uri? = null
|
var coverImageFile: File? = null
|
||||||
var isPointAvailable: Boolean? = null
|
var isPointAvailable: Boolean? = null
|
||||||
|
|
||||||
fun setAdult(isAdult: Boolean) {
|
fun setAdult(isAdult: Boolean) {
|
||||||
@@ -154,8 +154,8 @@ class AudioContentModifyViewModel(
|
|||||||
|
|
||||||
val requestJson = Gson().toJson(request)
|
val requestJson = Gson().toJson(request)
|
||||||
|
|
||||||
val coverImage = if (coverImageUri != null) {
|
val coverImage = if (coverImageFile != null) {
|
||||||
val file = File(getRealPathFromURI(coverImageUri!!))
|
val file = coverImageFile!!
|
||||||
MultipartBody.Part.createFormData(
|
MultipartBody.Part.createFormData(
|
||||||
"coverImage",
|
"coverImage",
|
||||||
file.name,
|
file.name,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.DatePickerDialog
|
import android.app.DatePickerDialog
|
||||||
import android.app.TimePickerDialog
|
import android.app.TimePickerDialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -15,8 +16,9 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import coil.load
|
import coil.load
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import coil.transform.RoundedCornersTransformation
|
import com.bumptech.glide.Glide
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.gun0912.tedpermission.PermissionListener
|
import com.gun0912.tedpermission.PermissionListener
|
||||||
import com.gun0912.tedpermission.normal.TedPermission
|
import com.gun0912.tedpermission.normal.TedPermission
|
||||||
import com.jakewharton.rxbinding4.widget.textChanges
|
import com.jakewharton.rxbinding4.widget.textChanges
|
||||||
@@ -26,6 +28,7 @@ import kr.co.vividnext.sodalive.R
|
|||||||
import kr.co.vividnext.sodalive.audio_content.PurchaseOption
|
import kr.co.vividnext.sodalive.audio_content.PurchaseOption
|
||||||
import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeFragment
|
import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeFragment
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
import kr.co.vividnext.sodalive.common.RealPathUtil
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
@@ -48,6 +51,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
private val viewModel: AudioContentUploadViewModel by inject()
|
private val viewModel: AudioContentUploadViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
private lateinit var cropper: ImagePickerCropper
|
||||||
|
|
||||||
private val themeFragment: AudioContentThemeFragment by lazy {
|
private val themeFragment: AudioContentThemeFragment by lazy {
|
||||||
AudioContentThemeFragment(
|
AudioContentThemeFragment(
|
||||||
@@ -66,35 +70,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val imageResult = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
val fileUri = data?.data
|
|
||||||
|
|
||||||
if (fileUri != null) {
|
|
||||||
binding.ivCover.background = null
|
|
||||||
binding.ivCover.load(fileUri) {
|
|
||||||
crossfade(true)
|
|
||||||
placeholder(R.drawable.ic_place_holder)
|
|
||||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
|
||||||
}
|
|
||||||
viewModel.coverImageUri = fileUri
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
"잘못된 파일입니다.\n다시 선택해 주세요.",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val selectAudioActivityResultLauncher = registerForActivityResult(
|
private val selectAudioActivityResultLauncher = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
) { result ->
|
) { result ->
|
||||||
@@ -113,18 +88,29 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
} else {
|
||||||
binding.tvSelectContent.text = "파일 선택"
|
binding.tvSelectContent.text = "파일 선택"
|
||||||
viewModel.contentUri = null
|
viewModel.contentUri = null
|
||||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
"파일 선택을 실패했습니다.\n다시 시도해 주세요.",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val datePickerDialogListener =
|
private val datePickerDialogListener =
|
||||||
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
|
||||||
viewModel.releaseDate = String.format("%d-%02d-%02d", year, monthOfYear + 1, dayOfMonth)
|
viewModel.releaseDate = String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
|
"%d-%02d-%02d",
|
||||||
|
year,
|
||||||
|
monthOfYear + 1,
|
||||||
|
dayOfMonth
|
||||||
|
)
|
||||||
viewModel.setReservationDate(
|
viewModel.setReservationDate(
|
||||||
String.format(
|
String.format(
|
||||||
|
Locale.getDefault(),
|
||||||
"%d.%02d.%02d",
|
"%d.%02d.%02d",
|
||||||
year,
|
year,
|
||||||
monthOfYear + 1,
|
monthOfYear + 1,
|
||||||
@@ -135,7 +121,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
|
|
||||||
private val timePickerDialogListener =
|
private val timePickerDialogListener =
|
||||||
TimePickerDialog.OnTimeSetListener { _, hourOfDay, minute ->
|
TimePickerDialog.OnTimeSetListener { _, hourOfDay, minute ->
|
||||||
val timeString = String.format("%02d:%02d", hourOfDay, minute)
|
val timeString = String.format(Locale.getDefault(), "%02d:%02d", hourOfDay, minute)
|
||||||
viewModel.releaseTime = timeString
|
viewModel.releaseTime = timeString
|
||||||
viewModel.setReservationTime(timeString.convertDateFormat("HH:mm", "a hh:mm"))
|
viewModel.setReservationTime(timeString.convertDateFormat("HH:mm", "a hh:mm"))
|
||||||
}
|
}
|
||||||
@@ -151,9 +137,45 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
bindData()
|
bindData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
cropper.cleanup()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
override fun setupView() {
|
override fun setupView() {
|
||||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
|
||||||
|
cropper = ImagePickerCropper(
|
||||||
|
caller = this,
|
||||||
|
context = this,
|
||||||
|
excludeGif = true,
|
||||||
|
isEnabledFreeStyleCrop = true,
|
||||||
|
config = ImagePickerCropper.Config(
|
||||||
|
aspectX = 1f, aspectY = 1f,
|
||||||
|
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
compressQuality = 90
|
||||||
|
),
|
||||||
|
onSuccess = { file, uri ->
|
||||||
|
binding.ivCover.background = null
|
||||||
|
Glide.with(this)
|
||||||
|
.load(uri)
|
||||||
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
|
.apply(
|
||||||
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
13.3f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(binding.ivCover)
|
||||||
|
|
||||||
|
viewModel.coverImageFile = file
|
||||||
|
},
|
||||||
|
onError = { e ->
|
||||||
|
Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
binding.tvServiceDate.text = if (SharedPreferenceManager.userId == 17958L) {
|
binding.tvServiceDate.text = if (SharedPreferenceManager.userId == 17958L) {
|
||||||
"※ 이용기간 : 대여(5일) | 소장(이용 기간 1년)"
|
"※ 이용기간 : 대여(5일) | 소장(이용 기간 1년)"
|
||||||
} else {
|
} else {
|
||||||
@@ -167,19 +189,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
themeFragment.show(supportFragmentManager, themeFragment.tag)
|
themeFragment.show(supportFragmentManager, themeFragment.tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.ivPhotoPicker.setOnClickListener {
|
binding.ivPhotoPicker.setOnClickListener { cropper.launch() }
|
||||||
ImagePicker.with(this)
|
|
||||||
.crop()
|
|
||||||
.galleryOnly()
|
|
||||||
.galleryMimeTypes( // Exclude gif images
|
|
||||||
mimeTypes = arrayOf(
|
|
||||||
"image/png",
|
|
||||||
"image/jpg",
|
|
||||||
"image/jpeg"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.createIntent { imageResult.launch(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.tvSelectContent.setOnClickListener {
|
binding.tvSelectContent.setOnClickListener {
|
||||||
val intent = Intent().apply {
|
val intent = Intent().apply {
|
||||||
@@ -359,7 +369,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
.subscribe {
|
.subscribe {
|
||||||
val price = it.toString().toIntOrNull()
|
val price = it.toString().toIntOrNull()
|
||||||
if (price != null) {
|
if (price != null) {
|
||||||
viewModel.price = price.toInt()
|
viewModel.price = price
|
||||||
} else {
|
} else {
|
||||||
viewModel.price = 0
|
viewModel.price = 0
|
||||||
if (it.isNotBlank()) {
|
if (it.isNotBlank()) {
|
||||||
@@ -377,7 +387,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
|||||||
.subscribe {
|
.subscribe {
|
||||||
val limited = it.toString().toIntOrNull()
|
val limited = it.toString().toIntOrNull()
|
||||||
if (limited != null) {
|
if (limited != null) {
|
||||||
viewModel.limited = limited.toInt()
|
viewModel.limited = limited
|
||||||
} else {
|
} else {
|
||||||
viewModel.limited = 0
|
viewModel.limited = 0
|
||||||
if (it.isNotBlank()) {
|
if (it.isNotBlank()) {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class AudioContentUploadViewModel(
|
|||||||
var releaseDate = ""
|
var releaseDate = ""
|
||||||
var releaseTime = ""
|
var releaseTime = ""
|
||||||
var theme: GetAudioContentThemeResponse? = null
|
var theme: GetAudioContentThemeResponse? = null
|
||||||
var coverImageUri: Uri? = null
|
var coverImageFile: File? = null
|
||||||
var contentUri: Uri? = null
|
var contentUri: Uri? = null
|
||||||
var previewStartTime: String? = null
|
var previewStartTime: String? = null
|
||||||
var previewEndTime: String? = null
|
var previewEndTime: String? = null
|
||||||
@@ -203,8 +203,8 @@ class AudioContentUploadViewModel(
|
|||||||
|
|
||||||
val requestJson = Gson().toJson(request)
|
val requestJson = Gson().toJson(request)
|
||||||
|
|
||||||
val coverImage = if (coverImageUri != null) {
|
val coverImage = if (coverImageFile != null) {
|
||||||
val file = File(getRealPathFromURI(coverImageUri!!))
|
val file = coverImageFile!!
|
||||||
MultipartBody.Part.createFormData(
|
MultipartBody.Part.createFormData(
|
||||||
"coverImage",
|
"coverImage",
|
||||||
file.name,
|
file.name,
|
||||||
@@ -323,7 +323,7 @@ class AudioContentUploadViewModel(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverImageUri == null) {
|
if (coverImageFile == null) {
|
||||||
_toastLiveData.postValue("커버이미지를 선택해 주세요.")
|
_toastLiveData.postValue("커버이미지를 선택해 주세요.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -413,7 +413,7 @@ class AudioContentUploadViewModel(
|
|||||||
|
|
||||||
// Check if the time difference is greater than 30 seconds (30000 milliseconds)
|
// Check if the time difference is greater than 30 seconds (30000 milliseconds)
|
||||||
return date2.time - date1.time
|
return date2.time - date1.time
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
// Handle invalid time formats or parsing errors
|
// Handle invalid time formats or parsing errors
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.provider.OpenableColumns
|
||||||
import androidx.activity.result.ActivityResultCaller
|
import androidx.activity.result.ActivityResultCaller
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.PickVisualMediaRequest
|
import androidx.activity.result.PickVisualMediaRequest
|
||||||
@@ -13,6 +14,7 @@ import com.yalantis.ucrop.UCrop
|
|||||||
import com.yalantis.ucrop.UCropActivity
|
import com.yalantis.ucrop.UCropActivity
|
||||||
import kr.co.vividnext.sodalive.BuildConfig
|
import kr.co.vividnext.sodalive.BuildConfig
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 단일 이미지 선택(13+ Photo Picker / 12- GetContent) → uCrop(기본 9:20) → [File, Uri] 반환
|
* 단일 이미지 선택(13+ Photo Picker / 12- GetContent) → uCrop(기본 9:20) → [File, Uri] 반환
|
||||||
@@ -20,8 +22,10 @@ import java.io.File
|
|||||||
* - 결과 파일은 cacheDir에 임시 생성 → 업로드 후 cleanup() 호출로 삭제
|
* - 결과 파일은 cacheDir에 임시 생성 → 업로드 후 cleanup() 호출로 삭제
|
||||||
*/
|
*/
|
||||||
class ImagePickerCropper(
|
class ImagePickerCropper(
|
||||||
private val caller: ActivityResultCaller,
|
caller: ActivityResultCaller,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val excludeGif: Boolean = false,
|
||||||
|
private val isEnabledFreeStyleCrop: Boolean = false,
|
||||||
private val config: Config = Config(),
|
private val config: Config = Config(),
|
||||||
private val onSuccess: (file: File, uri: Uri) -> Unit,
|
private val onSuccess: (file: File, uri: Uri) -> Unit,
|
||||||
private val onError: (Throwable) -> Unit = { it.printStackTrace() }
|
private val onError: (Throwable) -> Unit = { it.printStackTrace() }
|
||||||
@@ -41,15 +45,21 @@ class ImagePickerCropper(
|
|||||||
// 13+ : 시스템 Photo Picker
|
// 13+ : 시스템 Photo Picker
|
||||||
private val pickPhoto: ActivityResultLauncher<PickVisualMediaRequest> =
|
private val pickPhoto: ActivityResultLauncher<PickVisualMediaRequest> =
|
||||||
caller.registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
|
caller.registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
|
||||||
if (uri == null) onError(CancellationException("User cancelled picking."))
|
if (uri == null) onError(CancellationException("이미지 선택을 취소했습니다."))
|
||||||
else startCrop(uri)
|
else handlePickedUri(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 12- : SAF GetContent
|
// 12- : SAF GetContent
|
||||||
private val pickContent: ActivityResultLauncher<String> =
|
private val pickContent: ActivityResultLauncher<String> =
|
||||||
caller.registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
caller.registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||||
if (uri == null) onError(CancellationException("User cancelled picking."))
|
if (uri == null) onError(CancellationException("이미지 선택을 취소했습니다."))
|
||||||
else startCrop(uri)
|
else handlePickedUri(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val openDocument =
|
||||||
|
caller.registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||||
|
if (uri == null) onError(CancellationException("이미지 선택을 취소했습니다."))
|
||||||
|
else handlePickedUri(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
// uCrop 결과 수신
|
// uCrop 결과 수신
|
||||||
@@ -62,38 +72,98 @@ class ImagePickerCropper(
|
|||||||
if (out != null && file != null && file.exists()) {
|
if (out != null && file != null && file.exists()) {
|
||||||
onSuccess(file, out)
|
onSuccess(file, out)
|
||||||
} else {
|
} else {
|
||||||
onError(IllegalStateException("Crop finished but no output file/uri"))
|
onError(IllegalStateException("이미지 크롭을 실패했습니다.\n다시 시도해 주세요"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UCrop.RESULT_ERROR -> onError(
|
UCrop.RESULT_ERROR -> onError(
|
||||||
UCrop.getError(result.data!!) ?: RuntimeException("Crop error")
|
UCrop.getError(result.data!!)
|
||||||
|
?: RuntimeException("이미지 크롭을 실패했습니다.\n다시 시도해 주세요")
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> onError(CancellationException("User cancelled cropping."))
|
else -> onError(CancellationException("이미지 크롭을 취소했습니다."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 외부에서 호출: 선택 → 크롭 시작 */
|
/** 외부에서 호출: 선택 → 크롭 시작 */
|
||||||
fun launch() {
|
fun launch() {
|
||||||
if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable(context)) {
|
if (excludeGif) {
|
||||||
pickPhoto.launch(
|
openDocument.launch(arrayOf("image/png", "image/jpg", "image/jpeg"))
|
||||||
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
pickContent.launch("image/*")
|
if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable(context)) {
|
||||||
|
pickPhoto.launch(
|
||||||
|
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
pickContent.launch("image/*")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 마지막 크롭 결과 파일 (있으면) */
|
|
||||||
fun getCroppedFile(): File? = lastCroppedFile?.takeIf { it.exists() }
|
|
||||||
|
|
||||||
/** 임시 파일 삭제 */
|
/** 임시 파일 삭제 */
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
lastCroppedFile?.let { if (it.exists()) it.delete() }
|
lastCroppedFile?.let { if (it.exists()) it.delete() }
|
||||||
lastCroppedFile = null
|
lastCroppedFile = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handlePickedUri(source: Uri) {
|
||||||
|
if (isGifUri(source)) {
|
||||||
|
// 1) 캐시에 gif 그대로 복사
|
||||||
|
val gifFile = copyUriToCacheAsGif(source)
|
||||||
|
lastCroppedFile = gifFile
|
||||||
|
val fileUri = FileProvider.getUriForFile(
|
||||||
|
context, "${BuildConfig.APPLICATION_ID}.fileprovider", gifFile
|
||||||
|
)
|
||||||
|
// 2) 바로 반환 (크롭 생략)
|
||||||
|
onSuccess(gifFile, fileUri)
|
||||||
|
} else {
|
||||||
|
// 기존 그대로: uCrop 9:20 실행
|
||||||
|
startCrop(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isGifUri(uri: Uri): Boolean {
|
||||||
|
// 1순위: MIME type
|
||||||
|
val mime = context.contentResolver.getType(uri)
|
||||||
|
if (mime?.equals("image/gif", ignoreCase = true) == true) return true
|
||||||
|
|
||||||
|
// 2순위: 파일명 확장자
|
||||||
|
val name = getDisplayName(uri)?.lowercase()
|
||||||
|
return name?.endsWith(".gif") == true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDisplayName(uri: Uri): String? {
|
||||||
|
return context.contentResolver.query(
|
||||||
|
uri,
|
||||||
|
arrayOf(OpenableColumns.DISPLAY_NAME),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
?.use { c ->
|
||||||
|
val idx = c.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||||
|
if (idx >= 0 && c.moveToFirst()) c.getString(idx) else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyUriToCacheAsGif(uri: Uri): File {
|
||||||
|
val base = if (config.useExternalCache) context.externalCacheDir ?: context.cacheDir
|
||||||
|
else context.cacheDir
|
||||||
|
|
||||||
|
// 원본 이름 유지 시도, 실패하면 타임스탬프
|
||||||
|
val name = getDisplayName(uri)?.takeIf { it.endsWith(".gif", true) }
|
||||||
|
?: "picked_${System.currentTimeMillis()}.gif"
|
||||||
|
|
||||||
|
val outFile = File(base, name)
|
||||||
|
context.contentResolver.openInputStream(uri).use { input ->
|
||||||
|
requireNotNull(input) { "Cannot open input stream for $uri" }
|
||||||
|
FileOutputStream(outFile).use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outFile
|
||||||
|
}
|
||||||
|
|
||||||
private fun startCrop(source: Uri) {
|
private fun startCrop(source: Uri) {
|
||||||
val destFile = createTempCropFile()
|
val destFile = createTempCropFile()
|
||||||
lastCroppedFile = destFile
|
lastCroppedFile = destFile
|
||||||
@@ -110,8 +180,8 @@ class ImagePickerCropper(
|
|||||||
setCompressionQuality(config.compressQuality)
|
setCompressionQuality(config.compressQuality)
|
||||||
|
|
||||||
// 제스처/컨트롤 (필요시 조절)
|
// 제스처/컨트롤 (필요시 조절)
|
||||||
setFreeStyleCropEnabled(false)
|
setFreeStyleCropEnabled(isEnabledFreeStyleCrop)
|
||||||
setHideBottomControls(false)
|
setHideBottomControls(true)
|
||||||
setAllowedGestures(
|
setAllowedGestures(
|
||||||
UCropActivity.SCALE, // Aspect 줄이진 못하지만 확대/축소
|
UCropActivity.SCALE, // Aspect 줄이진 못하지만 확대/축소
|
||||||
UCropActivity.ROTATE, // 회전
|
UCropActivity.ROTATE, // 회전
|
||||||
|
|||||||
@@ -2,35 +2,29 @@ package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
|
||||||
import com.gun0912.tedpermission.PermissionListener
|
import com.gun0912.tedpermission.PermissionListener
|
||||||
import com.gun0912.tedpermission.normal.TedPermission
|
import com.gun0912.tedpermission.normal.TedPermission
|
||||||
import com.jakewharton.rxbinding4.widget.textChanges
|
import com.jakewharton.rxbinding4.widget.textChanges
|
||||||
import com.orhanobut.logger.Logger
|
|
||||||
import com.yalantis.ucrop.UCrop
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
import kr.co.vividnext.sodalive.common.Constants
|
import kr.co.vividnext.sodalive.common.Constants
|
||||||
|
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityModifyBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityModifyBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModifyBinding>(
|
class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModifyBinding>(
|
||||||
ActivityCreatorCommunityModifyBinding::inflate
|
ActivityCreatorCommunityModifyBinding::inflate
|
||||||
@@ -39,142 +33,11 @@ class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModi
|
|||||||
private val viewModel: CreatorCommunityModifyViewModel by inject()
|
private val viewModel: CreatorCommunityModifyViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
private lateinit var croppedTempFile: File
|
private lateinit var cropper: ImagePickerCropper
|
||||||
|
|
||||||
private val imageCropResult = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
val fileUri = UCrop.getOutput(data!!)
|
|
||||||
|
|
||||||
if (fileUri != null) {
|
|
||||||
handleCroppedImage(fileUri)
|
|
||||||
} else {
|
|
||||||
showWrongImageFile()
|
|
||||||
}
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
val cropError = UCrop.getError(data!!)
|
|
||||||
cropError?.printStackTrace()
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
cropError?.message,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val imageSelectResult = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
val fileUri = data?.data
|
|
||||||
|
|
||||||
if (fileUri != null) {
|
|
||||||
val mime = contentResolver.getType(fileUri)
|
|
||||||
|
|
||||||
if (mime.equals("image/gif", ignoreCase = true)) {
|
|
||||||
handleGifImage(fileUri)
|
|
||||||
} else {
|
|
||||||
launchCrop(fileUri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showWrongImageFile()
|
|
||||||
}
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
showImagePickerError(data = data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun launchImagePicker() {
|
|
||||||
ImagePicker.with(this)
|
|
||||||
.galleryOnly()
|
|
||||||
.createIntent { imageSelectResult.launch(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun launchCrop(fileUri: Uri) {
|
|
||||||
croppedTempFile = File.createTempFile("cropped_", ".jpg", cacheDir)
|
|
||||||
|
|
||||||
val destinationUri = Uri.fromFile(croppedTempFile)
|
|
||||||
|
|
||||||
val options = UCrop.Options().apply {
|
|
||||||
setFreeStyleCropEnabled(true)
|
|
||||||
setToolbarTitle("이미지 자르기")
|
|
||||||
}
|
|
||||||
|
|
||||||
val uCrop = UCrop.of(fileUri, destinationUri)
|
|
||||||
.withOptions(options)
|
|
||||||
.withMaxResultSize(1080, 1080)
|
|
||||||
|
|
||||||
imageCropResult.launch(uCrop.getIntent(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleGifImage(fileUri: Uri) {
|
|
||||||
binding.ivContent.background = null
|
|
||||||
|
|
||||||
Glide.with(this)
|
|
||||||
.asGif()
|
|
||||||
.load(fileUri)
|
|
||||||
.placeholder(R.drawable.ic_place_holder)
|
|
||||||
.apply(
|
|
||||||
RequestOptions().transform(
|
|
||||||
RoundedCorners(
|
|
||||||
8f.dpToPx().toInt()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.into(binding.ivContent)
|
|
||||||
|
|
||||||
viewModel.imageUri = fileUri
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleCroppedImage(fileUri: Uri) {
|
|
||||||
binding.ivContent.background = null
|
|
||||||
|
|
||||||
Glide.with(this)
|
|
||||||
.load(fileUri)
|
|
||||||
.placeholder(R.drawable.ic_place_holder)
|
|
||||||
.apply(
|
|
||||||
RequestOptions().transform(
|
|
||||||
RoundedCorners(
|
|
||||||
8f.dpToPx().toInt()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.into(binding.ivContent)
|
|
||||||
|
|
||||||
viewModel.imageUri = fileUri
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showWrongImageFile() {
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
"잘못된 파일입니다.\n다시 선택해 주세요.",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showImagePickerError(data: Intent?) {
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
ImagePicker.getError(data),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
checkPermissions()
|
checkPermissions()
|
||||||
|
|
||||||
viewModel.getRealPathFromURI = {
|
|
||||||
RealPathUtil.getRealPath(applicationContext, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindData()
|
bindData()
|
||||||
|
|
||||||
val postId = intent.getLongExtra(Constants.EXTRA_COMMUNITY_POST_ID, 0)
|
val postId = intent.getLongExtra(Constants.EXTRA_COMMUNITY_POST_ID, 0)
|
||||||
@@ -193,31 +56,47 @@ class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
deleteCroppedTempFile()
|
cropper.cleanup()
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteCroppedTempFile() {
|
|
||||||
try {
|
|
||||||
if (::croppedTempFile.isInitialized && croppedTempFile.exists()) {
|
|
||||||
val deleted = croppedTempFile.delete()
|
|
||||||
if (!deleted) {
|
|
||||||
Logger.w("임시 파일 삭제 실패: ${croppedTempFile.absolutePath}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setupView() {
|
override fun setupView() {
|
||||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
|
||||||
|
cropper = ImagePickerCropper(
|
||||||
|
caller = this,
|
||||||
|
context = this,
|
||||||
|
isEnabledFreeStyleCrop = true,
|
||||||
|
config = ImagePickerCropper.Config(
|
||||||
|
aspectX = 1f, aspectY = 1f,
|
||||||
|
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
compressQuality = 90
|
||||||
|
),
|
||||||
|
onSuccess = { file, uri ->
|
||||||
|
binding.ivContent.background = null
|
||||||
|
Glide.with(this)
|
||||||
|
.load(uri)
|
||||||
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
|
.apply(
|
||||||
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
16f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(binding.ivContent)
|
||||||
|
|
||||||
|
viewModel.imageFile = file
|
||||||
|
},
|
||||||
|
onError = { e ->
|
||||||
|
Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
binding.toolbar.tvBack.text = "게시글 등록"
|
binding.toolbar.tvBack.text = "게시글 등록"
|
||||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||||
|
|
||||||
binding.ivPhotoPicker.setOnClickListener { launchImagePicker() }
|
binding.ivPhotoPicker.setOnClickListener { cropper.launch() }
|
||||||
|
|
||||||
if (SharedPreferenceManager.isAuth) {
|
if (SharedPreferenceManager.isAuth) {
|
||||||
binding.llSetAdult.visibility = View.VISIBLE
|
binding.llSetAdult.visibility = View.VISIBLE
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify
|
package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
@@ -46,11 +45,9 @@ class CreatorCommunityModifyViewModel(
|
|||||||
val isAvailableCommentLiveData: LiveData<Boolean>
|
val isAvailableCommentLiveData: LiveData<Boolean>
|
||||||
get() = _isAvailableCommentLiveData
|
get() = _isAvailableCommentLiveData
|
||||||
|
|
||||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
|
||||||
|
|
||||||
var postId = 0L
|
var postId = 0L
|
||||||
var content = ""
|
var content = ""
|
||||||
var imageUri: Uri? = null
|
var imageFile: File? = null
|
||||||
private var communityPost: GetCommunityPostListResponse? = null
|
private var communityPost: GetCommunityPostListResponse? = null
|
||||||
|
|
||||||
fun setAdult(isAdult: Boolean) {
|
fun setAdult(isAdult: Boolean) {
|
||||||
@@ -134,8 +131,8 @@ class CreatorCommunityModifyViewModel(
|
|||||||
|
|
||||||
val requestJson = Gson().toJson(request)
|
val requestJson = Gson().toJson(request)
|
||||||
|
|
||||||
val postImage = if (imageUri != null) {
|
val postImage = if (imageFile != null) {
|
||||||
val file = File(getRealPathFromURI(imageUri!!))
|
val file = imageFile!!
|
||||||
MultipartBody.Part.createFormData(
|
MultipartBody.Part.createFormData(
|
||||||
"postImage",
|
"postImage",
|
||||||
file.name,
|
file.name,
|
||||||
|
|||||||
@@ -2,29 +2,24 @@ package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
|
||||||
import com.gun0912.tedpermission.PermissionListener
|
import com.gun0912.tedpermission.PermissionListener
|
||||||
import com.gun0912.tedpermission.normal.TedPermission
|
import com.gun0912.tedpermission.normal.TedPermission
|
||||||
import com.jakewharton.rxbinding4.widget.textChanges
|
import com.jakewharton.rxbinding4.widget.textChanges
|
||||||
import com.orhanobut.logger.Logger
|
|
||||||
import com.yalantis.ucrop.UCrop
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityWriteBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityWriteBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
@@ -38,154 +33,18 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
|
|||||||
private val viewModel: CreatorCommunityWriteViewModel by inject()
|
private val viewModel: CreatorCommunityWriteViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
private lateinit var croppedTempFile: File
|
|
||||||
|
|
||||||
private val imageCropResult = registerForActivityResult(
|
private lateinit var cropper: ImagePickerCropper
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
val fileUri = UCrop.getOutput(data!!)
|
|
||||||
|
|
||||||
if (fileUri != null) {
|
|
||||||
handleCroppedImage(fileUri)
|
|
||||||
} else {
|
|
||||||
showWrongImageFile()
|
|
||||||
}
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
val cropError = UCrop.getError(data!!)
|
|
||||||
cropError?.printStackTrace()
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
cropError?.message,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val imageSelectResult = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
val fileUri = data?.data
|
|
||||||
|
|
||||||
if (fileUri != null) {
|
|
||||||
val mime = contentResolver.getType(fileUri)
|
|
||||||
|
|
||||||
if (mime.equals("image/gif", ignoreCase = true)) {
|
|
||||||
handleGifImage(fileUri)
|
|
||||||
} else {
|
|
||||||
launchCrop(fileUri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showWrongImageFile()
|
|
||||||
}
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
showImagePickerError(data = data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun launchImagePicker() {
|
|
||||||
ImagePicker.with(this)
|
|
||||||
.galleryOnly()
|
|
||||||
.createIntent { imageSelectResult.launch(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun launchCrop(fileUri: Uri) {
|
|
||||||
croppedTempFile = File.createTempFile("cropped_", ".jpg", cacheDir)
|
|
||||||
|
|
||||||
val destinationUri = Uri.fromFile(croppedTempFile)
|
|
||||||
|
|
||||||
val options = UCrop.Options().apply {
|
|
||||||
setFreeStyleCropEnabled(true)
|
|
||||||
setToolbarTitle("이미지 자르기")
|
|
||||||
}
|
|
||||||
|
|
||||||
val uCrop = UCrop.of(fileUri, destinationUri)
|
|
||||||
.withOptions(options)
|
|
||||||
.withMaxResultSize(1080, 1080)
|
|
||||||
|
|
||||||
imageCropResult.launch(uCrop.getIntent(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleGifImage(fileUri: Uri) {
|
|
||||||
binding.llRecordAudio.visibility = View.VISIBLE
|
|
||||||
binding.ivContent.background = null
|
|
||||||
|
|
||||||
Glide.with(this)
|
|
||||||
.asGif()
|
|
||||||
.load(fileUri)
|
|
||||||
.placeholder(R.drawable.ic_place_holder)
|
|
||||||
.apply(
|
|
||||||
RequestOptions().transform(
|
|
||||||
RoundedCorners(
|
|
||||||
8f.dpToPx().toInt()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.into(binding.ivContent)
|
|
||||||
|
|
||||||
viewModel.setImageUri(fileUri)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleCroppedImage(fileUri: Uri) {
|
|
||||||
binding.llRecordAudio.visibility = View.VISIBLE
|
|
||||||
binding.ivContent.background = null
|
|
||||||
|
|
||||||
Glide.with(this)
|
|
||||||
.load(fileUri)
|
|
||||||
.placeholder(R.drawable.ic_place_holder)
|
|
||||||
.apply(
|
|
||||||
RequestOptions().transform(
|
|
||||||
RoundedCorners(
|
|
||||||
8f.dpToPx().toInt()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.into(binding.ivContent)
|
|
||||||
|
|
||||||
viewModel.setImageUri(fileUri)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showWrongImageFile() {
|
|
||||||
binding.llRecordAudio.visibility = View.GONE
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
"잘못된 파일입니다.\n다시 선택해 주세요.",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showImagePickerError(data: Intent?) {
|
|
||||||
binding.llRecordAudio.visibility = View.GONE
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
ImagePicker.getError(data),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
checkPermissions()
|
checkPermissions()
|
||||||
|
|
||||||
viewModel.getRealPathFromURI = {
|
|
||||||
RealPathUtil.getRealPath(applicationContext, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindData()
|
bindData()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
cropper.cleanup()
|
||||||
deleteAudioFile()
|
deleteAudioFile()
|
||||||
|
|
||||||
deleteCroppedTempFile()
|
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,22 +54,39 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteCroppedTempFile() {
|
|
||||||
try {
|
|
||||||
if (::croppedTempFile.isInitialized && croppedTempFile.exists()) {
|
|
||||||
val deleted = croppedTempFile.delete()
|
|
||||||
if (!deleted) {
|
|
||||||
Logger.w("임시 파일 삭제 실패: ${croppedTempFile.absolutePath}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setupView() {
|
override fun setupView() {
|
||||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
|
||||||
|
cropper = ImagePickerCropper(
|
||||||
|
caller = this,
|
||||||
|
context = this,
|
||||||
|
isEnabledFreeStyleCrop = true,
|
||||||
|
config = ImagePickerCropper.Config(
|
||||||
|
aspectX = 1f, aspectY = 1f,
|
||||||
|
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
compressQuality = 90
|
||||||
|
),
|
||||||
|
onSuccess = { file, uri ->
|
||||||
|
binding.ivContent.background = null
|
||||||
|
Glide.with(this)
|
||||||
|
.load(uri)
|
||||||
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
|
.apply(
|
||||||
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
16f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(binding.ivContent)
|
||||||
|
|
||||||
|
viewModel.setImageFile(file)
|
||||||
|
},
|
||||||
|
onError = { e ->
|
||||||
|
Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
binding.toolbar.tvBack.text = "게시글 등록"
|
binding.toolbar.tvBack.text = "게시글 등록"
|
||||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||||
|
|
||||||
@@ -220,7 +96,7 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
|
|||||||
fragment.show(supportFragmentManager, fragment.tag)
|
fragment.show(supportFragmentManager, fragment.tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.ivPhotoPicker.setOnClickListener { launchImagePicker() }
|
binding.ivPhotoPicker.setOnClickListener { cropper.launch() }
|
||||||
|
|
||||||
if (SharedPreferenceManager.isAuth) {
|
if (SharedPreferenceManager.isAuth) {
|
||||||
binding.llSetAdult.visibility = View.VISIBLE
|
binding.llSetAdult.visibility = View.VISIBLE
|
||||||
@@ -278,7 +154,7 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
|
|||||||
.subscribe {
|
.subscribe {
|
||||||
val price = it.toString().toIntOrNull()
|
val price = it.toString().toIntOrNull()
|
||||||
if (price != null) {
|
if (price != null) {
|
||||||
viewModel.price = price.toInt()
|
viewModel.price = price
|
||||||
} else {
|
} else {
|
||||||
viewModel.price = 0
|
viewModel.price = 0
|
||||||
if (it.isNotBlank()) {
|
if (it.isNotBlank()) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
|
package kr.co.vividnext.sodalive.explorer.profile.creator_community.write
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
@@ -45,12 +44,10 @@ class CreatorCommunityWriteViewModel(
|
|||||||
val isShowPriceUiLiveData: LiveData<Boolean>
|
val isShowPriceUiLiveData: LiveData<Boolean>
|
||||||
get() = _isShowPriceUiLiveData
|
get() = _isShowPriceUiLiveData
|
||||||
|
|
||||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
|
||||||
|
|
||||||
var price = 0
|
var price = 0
|
||||||
var content = ""
|
var content = ""
|
||||||
var audioFile: File? = null
|
var audioFile: File? = null
|
||||||
private var imageUri: Uri? = null
|
private var imageFile: File? = null
|
||||||
|
|
||||||
fun setAdult(isAdult: Boolean) {
|
fun setAdult(isAdult: Boolean) {
|
||||||
_isAdultLiveData.postValue(isAdult)
|
_isAdultLiveData.postValue(isAdult)
|
||||||
@@ -64,8 +61,8 @@ class CreatorCommunityWriteViewModel(
|
|||||||
_isPriceFreeLiveData.postValue(isPriceFree)
|
_isPriceFreeLiveData.postValue(isPriceFree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setImageUri(uri: Uri) {
|
fun setImageFile(file: File) {
|
||||||
this.imageUri = uri
|
this.imageFile = file
|
||||||
_isShowPriceUiLiveData.postValue(true)
|
_isShowPriceUiLiveData.postValue(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,8 +79,8 @@ class CreatorCommunityWriteViewModel(
|
|||||||
|
|
||||||
val requestJson = Gson().toJson(request)
|
val requestJson = Gson().toJson(request)
|
||||||
|
|
||||||
val postImage = if (imageUri != null) {
|
val postImage = if (imageFile != null) {
|
||||||
val file = File(getRealPathFromURI(imageUri!!))
|
val file = imageFile!!
|
||||||
MultipartBody.Part.createFormData(
|
MultipartBody.Part.createFormData(
|
||||||
"postImage",
|
"postImage",
|
||||||
file.name,
|
file.name,
|
||||||
@@ -188,12 +185,12 @@ class CreatorCommunityWriteViewModel(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageUri == null) {
|
if (imageFile == null) {
|
||||||
_toastLiveData.postValue("유료 게시글 등록을 위해서는 이미지가 필요합니다.")
|
_toastLiveData.postValue("유료 게시글 등록을 위해서는 이미지가 필요합니다.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
_toastLiveData.postValue("가격은 숫자만 입력 가능 합니다.")
|
_toastLiveData.postValue("가격은 숫자만 입력 가능 합니다.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,14 @@ package kr.co.vividnext.sodalive.live.room
|
|||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.Service
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
@@ -37,6 +36,7 @@ import androidx.activity.OnBackPressedCallback
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@@ -46,7 +46,6 @@ import com.bumptech.glide.load.DataSource
|
|||||||
import com.bumptech.glide.load.engine.GlideException
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.orhanobut.logger.Logger
|
import com.orhanobut.logger.Logger
|
||||||
import io.agora.rtc2.ClientRoleOptions
|
import io.agora.rtc2.ClientRoleOptions
|
||||||
@@ -67,8 +66,8 @@ import kr.co.vividnext.sodalive.agora.Agora
|
|||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
import kr.co.vividnext.sodalive.base.SodaDialog
|
import kr.co.vividnext.sodalive.base.SodaDialog
|
||||||
import kr.co.vividnext.sodalive.common.Constants
|
import kr.co.vividnext.sodalive.common.Constants
|
||||||
|
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.SodaLiveService
|
import kr.co.vividnext.sodalive.common.SodaLiveService
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityLiveRoomBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityLiveRoomBinding
|
||||||
@@ -117,6 +116,8 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
private lateinit var speakerListAdapter: LiveRoomProfileListAdapter
|
private lateinit var speakerListAdapter: LiveRoomProfileListAdapter
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
|
||||||
|
private lateinit var cropper: ImagePickerCropper
|
||||||
|
|
||||||
private lateinit var imm: InputMethodManager
|
private lateinit var imm: InputMethodManager
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
@@ -235,20 +236,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val imageResult =
|
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
// Image Uri will not be null for RESULT_OK
|
|
||||||
val fileUri = data?.data!!
|
|
||||||
roomInfoEditDialog.setCoverImageUri(fileUri)
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val rouletteConfigResult = registerForActivityResult(
|
private val rouletteConfigResult = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
) { result ->
|
) { result ->
|
||||||
@@ -280,9 +267,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||||
|
|
||||||
viewModel.getRealPathFromURI = {
|
|
||||||
RealPathUtil.getRealPath(applicationContext, it)
|
|
||||||
}
|
|
||||||
this.roomId = intent.getLongExtra(Constants.EXTRA_ROOM_ID, 0)
|
this.roomId = intent.getLongExtra(Constants.EXTRA_ROOM_ID, 0)
|
||||||
|
|
||||||
if (roomId <= 0) {
|
if (roomId <= 0) {
|
||||||
@@ -321,27 +305,34 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
bindData()
|
bindData()
|
||||||
|
|
||||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
|
||||||
|
cropper = ImagePickerCropper(
|
||||||
|
caller = this,
|
||||||
|
context = this,
|
||||||
|
excludeGif = true,
|
||||||
|
config = ImagePickerCropper.Config(
|
||||||
|
aspectX = 2f, aspectY = 3.8f,
|
||||||
|
maxWidth = 1080, maxHeight = 2052,
|
||||||
|
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
compressQuality = 90
|
||||||
|
),
|
||||||
|
onSuccess = { file, uri ->
|
||||||
|
roomInfoEditDialog.setCoverImageUri(file)
|
||||||
|
},
|
||||||
|
onError = { e ->
|
||||||
|
Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
imm = getSystemService(
|
imm = getSystemService(
|
||||||
Service.INPUT_METHOD_SERVICE
|
INPUT_METHOD_SERVICE
|
||||||
) as InputMethodManager
|
) as InputMethodManager
|
||||||
|
|
||||||
roomDialog = LiveRoomDialog(this, layoutInflater)
|
roomDialog = LiveRoomDialog(this, layoutInflater)
|
||||||
roomInfoEditDialog = LiveRoomInfoEditDialog(
|
roomInfoEditDialog = LiveRoomInfoEditDialog(
|
||||||
activity = this,
|
activity = this,
|
||||||
layoutInflater = layoutInflater,
|
layoutInflater = layoutInflater,
|
||||||
onClickImagePicker = {
|
onClickImagePicker = { cropper.launch() }
|
||||||
ImagePicker.with(this)
|
|
||||||
.crop()
|
|
||||||
.galleryOnly()
|
|
||||||
.galleryMimeTypes( // Exclude gif images
|
|
||||||
mimeTypes = arrayOf(
|
|
||||||
"image/png",
|
|
||||||
"image/jpg",
|
|
||||||
"image/jpeg"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.createIntent { imageResult.launch(it) }
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
roomProfileDialog = LiveRoomProfileDialog(
|
roomProfileDialog = LiveRoomProfileDialog(
|
||||||
layoutInflater = layoutInflater,
|
layoutInflater = layoutInflater,
|
||||||
@@ -559,6 +550,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
cropper.cleanup()
|
||||||
hideKeyboard {
|
hideKeyboard {
|
||||||
viewModel.quitRoom(roomId) {
|
viewModel.quitRoom(roomId) {
|
||||||
SodaLiveService.stopService(this)
|
SodaLiveService.stopService(this)
|
||||||
@@ -960,7 +952,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
)
|
)
|
||||||
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
|
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
|
||||||
roomInfoEditDialog.setMenuPreset(it)
|
roomInfoEditDialog.setMenuPreset(it)
|
||||||
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageUri, isActivateMenu, menuId, menu, isAdult, isEntryMessageEnabled ->
|
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageFile, isActivateMenu, menuId, menu, isAdult, isEntryMessageEnabled ->
|
||||||
if (isEntryMessageEnabled != null) {
|
if (isEntryMessageEnabled != null) {
|
||||||
this.isEntryMessageEnabled = isEntryMessageEnabled
|
this.isEntryMessageEnabled = isEntryMessageEnabled
|
||||||
}
|
}
|
||||||
@@ -969,12 +961,13 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
response.roomId,
|
response.roomId,
|
||||||
newTitle,
|
newTitle,
|
||||||
newContent,
|
newContent,
|
||||||
newCoverImageUri,
|
newCoverImageFile,
|
||||||
isActivateMenu,
|
isActivateMenu,
|
||||||
menuId,
|
menuId,
|
||||||
menu,
|
menu,
|
||||||
isAdult,
|
isAdult,
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
|
cropper.cleanup()
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
"라이브 정보가 수정되었습니다.",
|
"라이브 정보가 수정되었습니다.",
|
||||||
@@ -1280,7 +1273,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
val clickableSpan = object : ClickableSpan() {
|
val clickableSpan = object : ClickableSpan() {
|
||||||
override fun onClick(widget: View) {
|
override fun onClick(widget: View) {
|
||||||
val url = spannable.subSequence(start, end).toString()
|
val url = spannable.subSequence(start, end).toString()
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spannable.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
spannable.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package kr.co.vividnext.sodalive.live.room
|
package kr.co.vividnext.sodalive.live.room
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
@@ -99,8 +98,6 @@ class LiveRoomViewModel(
|
|||||||
val isSignatureOn: LiveData<Boolean>
|
val isSignatureOn: LiveData<Boolean>
|
||||||
get() = _isSignatureOn
|
get() = _isSignatureOn
|
||||||
|
|
||||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
|
||||||
|
|
||||||
private val blockedMemberIdList: MutableList<Long> = mutableListOf()
|
private val blockedMemberIdList: MutableList<Long> = mutableListOf()
|
||||||
|
|
||||||
val mutex = Mutex()
|
val mutex = Mutex()
|
||||||
@@ -430,7 +427,7 @@ class LiveRoomViewModel(
|
|||||||
roomId: Long,
|
roomId: Long,
|
||||||
newTitle: String,
|
newTitle: String,
|
||||||
newContent: String,
|
newContent: String,
|
||||||
newCoverImageUri: Uri? = null,
|
newCoverImageFile: File? = null,
|
||||||
isActivateMenu: Boolean?,
|
isActivateMenu: Boolean?,
|
||||||
menuId: Long,
|
menuId: Long,
|
||||||
menu: String,
|
menu: String,
|
||||||
@@ -462,7 +459,7 @@ class LiveRoomViewModel(
|
|||||||
request.notice == null &&
|
request.notice == null &&
|
||||||
menu == roomInfoResponse.menuPan &&
|
menu == roomInfoResponse.menuPan &&
|
||||||
request.isAdult == null &&
|
request.isAdult == null &&
|
||||||
newCoverImageUri == null &&
|
newCoverImageFile == null &&
|
||||||
request.isActiveMenuPan == null
|
request.isActiveMenuPan == null
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
@@ -480,8 +477,8 @@ class LiveRoomViewModel(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
val coverImage = if (newCoverImageUri != null) {
|
val coverImage = if (newCoverImageFile != null) {
|
||||||
val file = File(getRealPathFromURI(newCoverImageUri))
|
val file = newCoverImageFile
|
||||||
MultipartBody.Part.createFormData(
|
MultipartBody.Part.createFormData(
|
||||||
"coverImage",
|
"coverImage",
|
||||||
file.name,
|
file.name,
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||||||
cropper = ImagePickerCropper(
|
cropper = ImagePickerCropper(
|
||||||
caller = this,
|
caller = this,
|
||||||
context = this,
|
context = this,
|
||||||
|
excludeGif = true,
|
||||||
config = ImagePickerCropper.Config(
|
config = ImagePickerCropper.Config(
|
||||||
aspectX = 2f, aspectY = 3.8f,
|
aspectX = 2f, aspectY = 3.8f,
|
||||||
maxWidth = 1080, maxHeight = 2052,
|
maxWidth = 1080, maxHeight = 2052,
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package kr.co.vividnext.sodalive.live.room.update
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.net.Uri
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -15,15 +13,18 @@ import android.widget.Toast
|
|||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import coil.load
|
import com.bumptech.glide.Glide
|
||||||
import coil.transform.RoundedCornersTransformation
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomInfoUpdateBinding
|
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomInfoUpdateBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
|
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
|
||||||
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
|
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
class LiveRoomInfoEditDialog(
|
class LiveRoomInfoEditDialog(
|
||||||
@@ -35,7 +36,7 @@ class LiveRoomInfoEditDialog(
|
|||||||
private val dialogView = DialogLiveRoomInfoUpdateBinding.inflate(layoutInflater)
|
private val dialogView = DialogLiveRoomInfoUpdateBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
private var coverImageUrl: String? = null
|
private var coverImageUrl: String? = null
|
||||||
private var coverImageUri: Uri? = null
|
private var coverImageFile: File? = null
|
||||||
|
|
||||||
private var menuId = 0L
|
private var menuId = 0L
|
||||||
private val menuList = mutableListOf<GetMenuPresetResponse>()
|
private val menuList = mutableListOf<GetMenuPresetResponse>()
|
||||||
@@ -58,7 +59,7 @@ class LiveRoomInfoEditDialog(
|
|||||||
|
|
||||||
alertDialog = dialogBuilder.create()
|
alertDialog = dialogBuilder.create()
|
||||||
alertDialog.setCancelable(false)
|
alertDialog.setCancelable(false)
|
||||||
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
alertDialog.window?.setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
|
||||||
|
|
||||||
dialogView.ivPhotoPicker.setOnClickListener { onClickImagePicker() }
|
dialogView.ivPhotoPicker.setOnClickListener { onClickImagePicker() }
|
||||||
dialogView.ivClose.setOnClickListener { alertDialog.dismiss() }
|
dialogView.ivClose.setOnClickListener { alertDialog.dismiss() }
|
||||||
@@ -185,22 +186,34 @@ class LiveRoomInfoEditDialog(
|
|||||||
isEntryMessageEnabledLiveData.value = isEntryMessageEnabled
|
isEntryMessageEnabledLiveData.value = isEntryMessageEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCoverImageUri(coverImageUri: Uri) {
|
fun setCoverImageUri(file: File) {
|
||||||
this.coverImageUri = coverImageUri
|
this.coverImageFile = file
|
||||||
dialogView.ivCover.load(coverImageUri) {
|
Glide.with(activity)
|
||||||
crossfade(true)
|
.load(file)
|
||||||
placeholder(R.drawable.ic_place_holder)
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
.apply(
|
||||||
}
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
13.3f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(dialogView.ivCover)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCoverImageUrl(coverImageUrl: String) {
|
fun setCoverImageUrl(coverImageUrl: String) {
|
||||||
this.coverImageUrl = coverImageUrl
|
this.coverImageUrl = coverImageUrl
|
||||||
dialogView.ivCover.load(coverImageUrl) {
|
Glide.with(activity)
|
||||||
crossfade(true)
|
.load(coverImageUrl)
|
||||||
placeholder(R.drawable.ic_place_holder)
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
.apply(
|
||||||
}
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
13.3f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(dialogView.ivCover)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setMenuPreset(menuList: List<GetMenuPresetResponse>) {
|
fun setMenuPreset(menuList: List<GetMenuPresetResponse>) {
|
||||||
@@ -223,7 +236,7 @@ class LiveRoomInfoEditDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setConfirmAction(
|
fun setConfirmAction(
|
||||||
confirmAction: (String, String, Uri?, Boolean?, Long, String, Boolean?, Boolean?) -> Unit
|
confirmAction: (String, String, File?, Boolean?, Long, String, Boolean?, Boolean?) -> Unit
|
||||||
) {
|
) {
|
||||||
dialogView.tvConfirm.setOnClickListener {
|
dialogView.tvConfirm.setOnClickListener {
|
||||||
alertDialog.dismiss()
|
alertDialog.dismiss()
|
||||||
@@ -235,7 +248,7 @@ class LiveRoomInfoEditDialog(
|
|||||||
confirmAction(
|
confirmAction(
|
||||||
newTitle,
|
newTitle,
|
||||||
newContent,
|
newContent,
|
||||||
coverImageUri,
|
coverImageFile,
|
||||||
if (isActivateMenu != null) {
|
if (isActivateMenu != null) {
|
||||||
isActivateMenu
|
isActivateMenu
|
||||||
} else if (
|
} else if (
|
||||||
@@ -262,7 +275,7 @@ class LiveRoomInfoEditDialog(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
coverImageUri = null
|
coverImageFile = null
|
||||||
coverImageUrl = null
|
coverImageUrl = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,7 +304,7 @@ class LiveRoomInfoEditDialog(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (menuList.size > 0) {
|
if (menuList.isNotEmpty()) {
|
||||||
dialogView.llSelectMenu2.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
|
dialogView.llSelectMenu2.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
|
||||||
dialogView.tvSelectMenu2.setTextColor(
|
dialogView.tvSelectMenu2.setTextColor(
|
||||||
ContextCompat.getColor(
|
ContextCompat.getColor(
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
package kr.co.vividnext.sodalive.mypage.profile
|
package kr.co.vividnext.sodalive.mypage.profile
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import coil.load
|
import coil.load
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.jakewharton.rxbinding4.widget.textChanges
|
import com.jakewharton.rxbinding4.widget.textChanges
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityProfileUpdateBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityProfileUpdateBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemLiveTagSelectedBinding
|
import kr.co.vividnext.sodalive.databinding.ItemLiveTagSelectedBinding
|
||||||
@@ -33,27 +35,6 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
|
|||||||
|
|
||||||
private val viewModel: ProfileUpdateViewModel by inject()
|
private val viewModel: ProfileUpdateViewModel by inject()
|
||||||
|
|
||||||
private val imageResult =
|
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
val resultCode = result.resultCode
|
|
||||||
val data = result.data
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
// Image Uri will not be null for RESULT_OK
|
|
||||||
val fileUri = data?.data!!
|
|
||||||
binding.ivProfile.background = null
|
|
||||||
viewModel.updateProfileImage(fileUri) {
|
|
||||||
SharedPreferenceManager.profileImage = it
|
|
||||||
binding.ivProfile.load(it) {
|
|
||||||
crossfade(true)
|
|
||||||
transformations(CircleCropTransformation())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
|
||||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val tagFragment: MemberTagFragment by lazy {
|
private val tagFragment: MemberTagFragment by lazy {
|
||||||
MemberTagFragment(viewModel.tags) { tag, isChecked ->
|
MemberTagFragment(viewModel.tags) { tag, isChecked ->
|
||||||
when {
|
when {
|
||||||
@@ -76,22 +57,23 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
|
|||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
|
||||||
|
private lateinit var cropper: ImagePickerCropper
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
viewModel.getRealPathFromURI = {
|
|
||||||
RealPathUtil.getRealPath(applicationContext, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindData()
|
bindData()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
|
||||||
viewModel.getUserInfo()
|
viewModel.getUserInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
cropper.cleanup()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
private fun bindData() {
|
private fun bindData() {
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
binding.etBlog.textChanges().skip(1)
|
binding.etBlog.textChanges().skip(1)
|
||||||
@@ -220,6 +202,34 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
|
|||||||
|
|
||||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
|
||||||
|
cropper = ImagePickerCropper(
|
||||||
|
caller = this,
|
||||||
|
context = this,
|
||||||
|
isEnabledFreeStyleCrop = false,
|
||||||
|
config = ImagePickerCropper.Config(
|
||||||
|
aspectX = 1f, aspectY = 1f,
|
||||||
|
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
compressQuality = 90
|
||||||
|
),
|
||||||
|
onSuccess = { file, _ ->
|
||||||
|
binding.ivProfile.background = null
|
||||||
|
viewModel.updateProfileImage(file) {
|
||||||
|
SharedPreferenceManager.profileImage = it
|
||||||
|
Glide.with(this)
|
||||||
|
.load(it)
|
||||||
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
|
.apply(
|
||||||
|
RequestOptions()
|
||||||
|
.transform(CircleCrop())
|
||||||
|
)
|
||||||
|
.into(binding.ivProfile)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError = { e ->
|
||||||
|
Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
binding.etIntroduce.textChanges()
|
binding.etIntroduce.textChanges()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
@@ -241,19 +251,7 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
|
|||||||
viewModel.changeGender(Gender.NONE)
|
viewModel.changeGender(Gender.NONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.ivPhotoPicker.setOnClickListener {
|
binding.ivPhotoPicker.setOnClickListener { cropper.launch() }
|
||||||
ImagePicker.with(this)
|
|
||||||
.crop()
|
|
||||||
.galleryOnly()
|
|
||||||
.galleryMimeTypes( // Exclude gif images
|
|
||||||
mimeTypes = arrayOf(
|
|
||||||
"image/png",
|
|
||||||
"image/jpg",
|
|
||||||
"image/jpeg"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.createIntent { imageResult.launch(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.tvSelectTag.setOnClickListener {
|
binding.tvSelectTag.setOnClickListener {
|
||||||
if (tagFragment.isAdded) return@setOnClickListener
|
if (tagFragment.isAdded) return@setOnClickListener
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package kr.co.vividnext.sodalive.mypage.profile
|
package kr.co.vividnext.sodalive.mypage.profile
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.orhanobut.logger.Logger
|
import com.orhanobut.logger.Logger
|
||||||
@@ -53,8 +52,6 @@ class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewM
|
|||||||
val isLoading: LiveData<Boolean>
|
val isLoading: LiveData<Boolean>
|
||||||
get() = _isLoading
|
get() = _isLoading
|
||||||
|
|
||||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
|
||||||
|
|
||||||
fun getUserInfo() {
|
fun getUserInfo() {
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
repository.getProfile("Bearer ${SharedPreferenceManager.token}")
|
repository.getProfile("Bearer ${SharedPreferenceManager.token}")
|
||||||
@@ -90,8 +87,7 @@ class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewM
|
|||||||
_genderLiveData.postValue(gender)
|
_genderLiveData.postValue(gender)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateProfileImage(uri: Uri, onSuccess: (String) -> Unit) {
|
fun updateProfileImage(file: File, onSuccess: (String) -> Unit) {
|
||||||
val file = File(getRealPathFromURI(uri))
|
|
||||||
val image = MultipartBody.Part.createFormData(
|
val image = MultipartBody.Part.createFormData(
|
||||||
"image",
|
"image",
|
||||||
file.name,
|
file.name,
|
||||||
|
|||||||
Reference in New Issue
Block a user