fix: 라이브 생성 이미지 선택
- 이미지 선택 및 Crop 방법 변경
This commit is contained in:
@@ -149,7 +149,7 @@ dependencies {
|
|||||||
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.dhaval2404:imagepicker:2.1'
|
||||||
implementation 'com.github.yalantis:ucrop:2.2.10'
|
implementation 'com.github.yalantis:ucrop:2.2.11'
|
||||||
implementation 'com.github.zhpanvip:bannerviewpager:3.5.7'
|
implementation 'com.github.zhpanvip:bannerviewpager:3.5.7'
|
||||||
|
|
||||||
implementation 'com.google.android.gms:play-services-oss-licenses:17.1.0'
|
implementation 'com.google.android.gms:play-services-oss-licenses:17.1.0'
|
||||||
|
|||||||
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
@@ -239,3 +239,7 @@
|
|||||||
-dontwarn org.openjsse.**
|
-dontwarn org.openjsse.**
|
||||||
|
|
||||||
-keep interface kr.co.vividnext.sodalive.tracking.UserEventApi
|
-keep interface kr.co.vividnext.sodalive.tracking.UserEventApi
|
||||||
|
|
||||||
|
-dontwarn com.yalantis.ucrop**
|
||||||
|
-keep class com.yalantis.ucrop** { *; }
|
||||||
|
-keep interface com.yalantis.ucrop** { *; }
|
||||||
|
|||||||
@@ -289,5 +289,17 @@
|
|||||||
<activity android:name=".chat.character.detail.CharacterDetailActivity" />
|
<activity android:name=".chat.character.detail.CharacterDetailActivity" />
|
||||||
|
|
||||||
<activity android:name=".chat.talk.room.ChatRoomActivity" />
|
<activity android:name=".chat.talk.room.ChatRoomActivity" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
|
||||||
|
<!-- ★ 이 meta-data가 꼭 필요 -->
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package kr.co.vividnext.sodalive.common
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.result.ActivityResultCaller
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.PickVisualMediaRequest
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import com.yalantis.ucrop.UCrop
|
||||||
|
import com.yalantis.ucrop.UCropActivity
|
||||||
|
import kr.co.vividnext.sodalive.BuildConfig
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 단일 이미지 선택(13+ Photo Picker / 12- GetContent) → uCrop(기본 9:20) → [File, Uri] 반환
|
||||||
|
* - 갤러리만 사용(카메라 X) → 런타임 권한 불필요
|
||||||
|
* - 결과 파일은 cacheDir에 임시 생성 → 업로드 후 cleanup() 호출로 삭제
|
||||||
|
*/
|
||||||
|
class ImagePickerCropper(
|
||||||
|
private val caller: ActivityResultCaller,
|
||||||
|
private val context: Context,
|
||||||
|
private val config: Config = Config(),
|
||||||
|
private val onSuccess: (file: File, uri: Uri) -> Unit,
|
||||||
|
private val onError: (Throwable) -> Unit = { it.printStackTrace() }
|
||||||
|
) {
|
||||||
|
data class Config(
|
||||||
|
val aspectX: Float = 9f, // 고정 비율: 가로
|
||||||
|
val aspectY: Float = 20f, // 고정 비율: 세로
|
||||||
|
val maxWidth: Int? = null, // 예: 1080
|
||||||
|
val maxHeight: Int? = null, // 예: 2400
|
||||||
|
val compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
val compressQuality: Int = 90, // 0~100
|
||||||
|
val useExternalCache: Boolean = false // 외부 캐시 사용 여부
|
||||||
|
)
|
||||||
|
|
||||||
|
private var lastCroppedFile: File? = null
|
||||||
|
|
||||||
|
// 13+ : 시스템 Photo Picker
|
||||||
|
private val pickPhoto: ActivityResultLauncher<PickVisualMediaRequest> =
|
||||||
|
caller.registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
|
||||||
|
if (uri == null) onError(CancellationException("User cancelled picking."))
|
||||||
|
else startCrop(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12- : SAF GetContent
|
||||||
|
private val pickContent: ActivityResultLauncher<String> =
|
||||||
|
caller.registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||||
|
if (uri == null) onError(CancellationException("User cancelled picking."))
|
||||||
|
else startCrop(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// uCrop 결과 수신
|
||||||
|
private val cropResult =
|
||||||
|
caller.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
when (result.resultCode) {
|
||||||
|
Activity.RESULT_OK -> {
|
||||||
|
val out = UCrop.getOutput(result.data!!)
|
||||||
|
val file = lastCroppedFile
|
||||||
|
if (out != null && file != null && file.exists()) {
|
||||||
|
onSuccess(file, out)
|
||||||
|
} else {
|
||||||
|
onError(IllegalStateException("Crop finished but no output file/uri"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UCrop.RESULT_ERROR -> onError(
|
||||||
|
UCrop.getError(result.data!!) ?: RuntimeException("Crop error")
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> onError(CancellationException("User cancelled cropping."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 외부에서 호출: 선택 → 크롭 시작 */
|
||||||
|
fun launch() {
|
||||||
|
if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable(context)) {
|
||||||
|
pickPhoto.launch(
|
||||||
|
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
pickContent.launch("image/*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 마지막 크롭 결과 파일 (있으면) */
|
||||||
|
fun getCroppedFile(): File? = lastCroppedFile?.takeIf { it.exists() }
|
||||||
|
|
||||||
|
/** 임시 파일 삭제 */
|
||||||
|
fun cleanup() {
|
||||||
|
lastCroppedFile?.let { if (it.exists()) it.delete() }
|
||||||
|
lastCroppedFile = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startCrop(source: Uri) {
|
||||||
|
val destFile = createTempCropFile()
|
||||||
|
lastCroppedFile = destFile
|
||||||
|
|
||||||
|
val destUri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.fileprovider", // ★ Manifest와 동일
|
||||||
|
destFile
|
||||||
|
)
|
||||||
|
|
||||||
|
val options = UCrop.Options().apply {
|
||||||
|
// 압축 포맷 & 품질 (uCrop 2.2.11 기준)
|
||||||
|
setCompressionFormat(config.compressFormat)
|
||||||
|
setCompressionQuality(config.compressQuality)
|
||||||
|
|
||||||
|
// 제스처/컨트롤 (필요시 조절)
|
||||||
|
setFreeStyleCropEnabled(false)
|
||||||
|
setHideBottomControls(false)
|
||||||
|
setAllowedGestures(
|
||||||
|
UCropActivity.SCALE, // Aspect 줄이진 못하지만 확대/축소
|
||||||
|
UCropActivity.ROTATE, // 회전
|
||||||
|
UCropActivity.ALL
|
||||||
|
)
|
||||||
|
|
||||||
|
// (선택) UI 커스텀: 툴바/상태바/위젯 컬러 등
|
||||||
|
// setToolbarColor(...)
|
||||||
|
// setStatusBarColor(...)
|
||||||
|
// setActiveControlsWidgetColor(...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var u = UCrop.of(source, destUri)
|
||||||
|
.withAspectRatio(config.aspectX, config.aspectY)
|
||||||
|
.withOptions(options)
|
||||||
|
|
||||||
|
if (config.maxWidth != null && config.maxHeight != null) {
|
||||||
|
u = u.withMaxResultSize(config.maxWidth, config.maxHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
cropResult.launch(u.getIntent(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTempCropFile(): File {
|
||||||
|
val base = if (config.useExternalCache) context.externalCacheDir ?: context.cacheDir
|
||||||
|
else context.cacheDir
|
||||||
|
|
||||||
|
val ext = when (config.compressFormat) {
|
||||||
|
Bitmap.CompressFormat.PNG -> "png"
|
||||||
|
Bitmap.CompressFormat.WEBP -> "webp" // 일부 기기에서 deprecated 경고 가능
|
||||||
|
else -> "jpg" // JPEG default
|
||||||
|
}
|
||||||
|
return File(base, "crop_${System.currentTimeMillis()}.$ext")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CancellationException(msg: String) : RuntimeException(msg)
|
||||||
@@ -4,6 +4,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.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@@ -13,20 +14,19 @@ import android.widget.ImageView
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
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.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import coil.load
|
import com.bumptech.glide.Glide
|
||||||
import coil.transform.RoundedCornersTransformation
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
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.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.ActivityLiveRoomCreateBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityLiveRoomCreateBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemLiveTagSelectedBinding
|
import kr.co.vividnext.sodalive.databinding.ItemLiveTagSelectedBinding
|
||||||
@@ -47,6 +47,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||||||
private val viewModel: LiveRoomCreateViewModel by inject()
|
private val viewModel: LiveRoomCreateViewModel by inject()
|
||||||
|
|
||||||
private lateinit var loadingDialog: LoadingDialog
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
private lateinit var cropper: ImagePickerCropper
|
||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
@@ -95,34 +96,8 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.ivCover.background = null
|
|
||||||
binding.ivCover.load(fileUri) {
|
|
||||||
crossfade(true)
|
|
||||||
placeholder(R.drawable.ic_place_holder)
|
|
||||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
|
||||||
}
|
|
||||||
viewModel.coverImageUri = fileUri
|
|
||||||
viewModel.coverImagePath = null
|
|
||||||
} 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)
|
||||||
|
|
||||||
viewModel.getRealPathFromURI = {
|
|
||||||
RealPathUtil.getRealPath(applicationContext, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindData()
|
bindData()
|
||||||
|
|
||||||
viewModel.setTimeNow(
|
viewModel.setTimeNow(
|
||||||
@@ -132,25 +107,48 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||||||
viewModel.getAllMenuPreset()
|
viewModel.getAllMenuPreset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
cropper.cleanup()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
|
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
|
||||||
override fun setupView() {
|
override fun setupView() {
|
||||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
|
||||||
|
cropper = ImagePickerCropper(
|
||||||
|
caller = this,
|
||||||
|
context = this,
|
||||||
|
config = ImagePickerCropper.Config(
|
||||||
|
aspectX = 2f, aspectY = 3.8f,
|
||||||
|
maxWidth = 1080, maxHeight = 2052,
|
||||||
|
compressFormat = Bitmap.CompressFormat.JPEG,
|
||||||
|
compressQuality = 90
|
||||||
|
),
|
||||||
|
onSuccess = { file, uri ->
|
||||||
|
binding.ivCover.background = null
|
||||||
|
Glide.with(binding.ivCover.context)
|
||||||
|
.load(uri)
|
||||||
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
|
.apply(
|
||||||
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
16f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(binding.ivCover)
|
||||||
|
|
||||||
|
viewModel.coverImageFile = file
|
||||||
|
viewModel.coverImagePath = null
|
||||||
|
},
|
||||||
|
onError = { e ->
|
||||||
|
Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
binding.tvBack.setOnClickListener { finish() }
|
binding.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.llOpen.setOnClickListener {
|
binding.llOpen.setOnClickListener {
|
||||||
viewModel.setRoomType(LiveRoomType.OPEN)
|
viewModel.setRoomType(LiveRoomType.OPEN)
|
||||||
@@ -239,6 +237,8 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||||||
binding.tvMakeRoom.setOnClickListener {
|
binding.tvMakeRoom.setOnClickListener {
|
||||||
binding.tvMakeRoom.isEnabled = false
|
binding.tvMakeRoom.isEnabled = false
|
||||||
viewModel.createLiveRoom {
|
viewModel.createLiveRoom {
|
||||||
|
cropper.cleanup()
|
||||||
|
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
if (it.id != null) {
|
if (it.id != null) {
|
||||||
intent.putExtra(Constants.EXTRA_ROOM_ID, it.id)
|
intent.putExtra(Constants.EXTRA_ROOM_ID, it.id)
|
||||||
@@ -283,11 +283,17 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||||||
binding.etNotice.setText(it.notice)
|
binding.etNotice.setText(it.notice)
|
||||||
binding.etNumberOfPeople.setText(it.numberOfPeople.toString())
|
binding.etNumberOfPeople.setText(it.numberOfPeople.toString())
|
||||||
binding.ivCover.background = null
|
binding.ivCover.background = null
|
||||||
binding.ivCover.load(it.coverImageUrl) {
|
Glide.with(binding.ivCover.context)
|
||||||
crossfade(true)
|
.load(it.coverImageUrl)
|
||||||
placeholder(R.drawable.ic_place_holder)
|
.placeholder(R.drawable.ic_place_holder)
|
||||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
.apply(
|
||||||
}
|
RequestOptions().transform(
|
||||||
|
RoundedCorners(
|
||||||
|
16f.dpToPx().toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.into(binding.ivCover)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,15 +79,13 @@ class LiveRoomCreateViewModel(
|
|||||||
val menuLiveData: LiveData<String>
|
val menuLiveData: LiveData<String>
|
||||||
get() = _menuLiveData
|
get() = _menuLiveData
|
||||||
|
|
||||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
|
||||||
|
|
||||||
var title = ""
|
var title = ""
|
||||||
var content = ""
|
var content = ""
|
||||||
var numberOfPeople = 0
|
var numberOfPeople = 0
|
||||||
var tags = mutableSetOf<String>()
|
var tags = mutableSetOf<String>()
|
||||||
var beginDate = ""
|
var beginDate = ""
|
||||||
var beginTime = ""
|
var beginTime = ""
|
||||||
var coverImageUri: Uri? = null
|
var coverImageFile: File? = null
|
||||||
var coverImagePath: String? = null
|
var coverImagePath: String? = null
|
||||||
var password: String? = null
|
var password: String? = null
|
||||||
|
|
||||||
@@ -157,8 +155,8 @@ class LiveRoomCreateViewModel(
|
|||||||
|
|
||||||
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,
|
||||||
@@ -226,7 +224,7 @@ class LiveRoomCreateViewModel(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverImageUri == null && coverImagePath == null) {
|
if (coverImageFile == null && coverImagePath == null) {
|
||||||
_toastLiveData.postValue("커버이미지를 선택해주세요.")
|
_toastLiveData.postValue("커버이미지를 선택해주세요.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -273,7 +271,7 @@ class LiveRoomCreateViewModel(
|
|||||||
.subscribe(
|
.subscribe(
|
||||||
{
|
{
|
||||||
if (it.success && it.data != null) {
|
if (it.success && it.data != null) {
|
||||||
coverImageUri = null
|
coverImageFile = null
|
||||||
coverImagePath = it.data.coverImagePath
|
coverImagePath = it.data.coverImagePath
|
||||||
onSuccess(it.data!!)
|
onSuccess(it.data!!)
|
||||||
|
|
||||||
|
|||||||
@@ -62,35 +62,38 @@
|
|||||||
android:layout_marginHorizontal="13.3dp"
|
android:layout_marginHorizontal="13.3dp"
|
||||||
android:layout_marginTop="13.3dp"
|
android:layout_marginTop="13.3dp"
|
||||||
android:fontFamily="@font/gmarket_sans_bold"
|
android:fontFamily="@font/gmarket_sans_bold"
|
||||||
android:text="썸네일"
|
android:text="배경"
|
||||||
android:textColor="@color/color_eeeeee"
|
android:textColor="@color/color_eeeeee"
|
||||||
android:textSize="16.7sp" />
|
android:textSize="16.7sp" />
|
||||||
|
|
||||||
<RelativeLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="96.7dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="116.8dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="13.3dp">
|
android:layout_marginTop="13.3dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/iv_cover"
|
android:id="@+id/iv_cover"
|
||||||
android:layout_width="80dp"
|
android:layout_width="90dp"
|
||||||
android:layout_height="116.8dp"
|
android:layout_height="171dp"
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:background="@drawable/bg_round_corner_13_3_13181b"
|
android:background="@drawable/bg_round_corner_13_3_13181b"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:src="@drawable/ic_logo" />
|
android:src="@drawable/ic_logo"
|
||||||
|
app:layout_constraintDimensionRatio="2:3.8"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/iv_photo_picker"
|
android:id="@+id/iv_photo_picker"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_marginStart="-20dp"
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
android:background="@drawable/bg_round_corner_33_3_3bb9f1"
|
android:background="@drawable/bg_round_corner_33_3_3bb9f1"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:src="@drawable/ic_camera" />
|
android:src="@drawable/ic_camera"
|
||||||
</RelativeLayout>
|
app:layout_constraintBottom_toBottomOf="@+id/iv_cover"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/iv_cover" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
6
app/src/main/res/xml/file_paths.xml
Normal file
6
app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<!-- 임시 크롭 파일을 cache 디렉터리에 만들고 공유 -->
|
||||||
|
<cache-path name="cache" path="." />
|
||||||
|
<external-cache-path name="external_cache" path="." />
|
||||||
|
</paths>
|
||||||
Reference in New Issue
Block a user