오디션 지원 기능 추가

This commit is contained in:
klaus 2025-01-03 07:48:34 +09:00
parent c6ef5970a5
commit 4e14765e94
13 changed files with 632 additions and 2 deletions

View File

@ -7,11 +7,14 @@ import kr.co.vividnext.sodalive.audition.role.AuditionRoleDetailViewModel
import kr.co.vividnext.sodalive.audition.role.GetAuditionRoleDetailResponse import kr.co.vividnext.sodalive.audition.role.GetAuditionRoleDetailResponse
import kr.co.vividnext.sodalive.audition.vote.VoteAuditionApplicantRequest import kr.co.vividnext.sodalive.audition.vote.VoteAuditionApplicantRequest
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.room.menu.UpdateLiveMenuRequest import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.Path import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
@ -44,6 +47,14 @@ interface AuditionApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetAuditionApplicantListResponse>> ): Single<ApiResponse<GetAuditionApplicantListResponse>>
@POST("/audition/applicant")
@Multipart
fun applyAudition(
@Part contentFile: MultipartBody.Part,
@Part("request") request: RequestBody,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@POST("/audition/vote") @POST("/audition/vote")
fun voteApplicant( fun voteApplicant(
@Body request: VoteAuditionApplicantRequest, @Body request: VoteAuditionApplicantRequest,

View File

@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.audition
import kr.co.vividnext.sodalive.audition.role.AuditionRoleDetailViewModel import kr.co.vividnext.sodalive.audition.role.AuditionRoleDetailViewModel
import kr.co.vividnext.sodalive.audition.vote.VoteAuditionApplicantRequest import kr.co.vividnext.sodalive.audition.vote.VoteAuditionApplicantRequest
import okhttp3.MultipartBody
import okhttp3.RequestBody
class AuditionRepository( class AuditionRepository(
private val api: AuditionApi private val api: AuditionApi
@ -53,4 +55,14 @@ class AuditionRepository(
request = request, request = request,
authHeader = token authHeader = token
) )
fun applyAudition(
contentFile: MultipartBody.Part,
request: RequestBody,
token: String
) = api.applyAudition(
contentFile = contentFile,
request = request,
authHeader = token
)
} }

View File

@ -0,0 +1,55 @@
package kr.co.vividnext.sodalive.audition.applicant
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import kr.co.vividnext.sodalive.databinding.DialogApplicationMethodBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class ApplicationMethodDialog(
activity: Activity,
layoutInflater: LayoutInflater,
onClickFileUpload: () -> Unit,
onClickRecord: () -> Unit
) {
private val alertDialog: AlertDialog
val dialogView = DialogApplicationMethodBinding.inflate(layoutInflater)
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialogView.ivClose.setOnClickListener {
alertDialog.dismiss()
}
dialogView.llUploadFile.setOnClickListener {
alertDialog.dismiss()
onClickFileUpload()
}
dialogView.llRecord.setOnClickListener {
alertDialog.dismiss()
onClickRecord()
}
}
fun show(width: Int) {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = width - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
}

View File

@ -0,0 +1,10 @@
package kr.co.vividnext.sodalive.audition.applicant
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
@Keep
data class ApplyAuditionRoleRequest(
@SerializedName("roleId") val roleId: Long,
@SerializedName("phoneNumber") val phoneNumber: String
)

View File

@ -0,0 +1,83 @@
package kr.co.vividnext.sodalive.audition.applicant
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.databinding.FragmentAuditionApplyDialogBinding
class AuditionApplyDialogFragment(
private val fileName: String,
private val onClickApply: (String) -> Unit,
private val onClickClose: () -> Unit
) : BottomSheetDialogFragment() {
private lateinit var binding: FragmentAuditionApplyDialogBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener {
val d = it as BottomSheetDialog
val bottomSheet = d.findViewById<FrameLayout>(
com.google.android.material.R.id.design_bottom_sheet
)
if (bottomSheet != null) {
val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
bottomSheetBehavior.isDraggable = false
}
}
return dialog
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAuditionApplyDialogBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.ivClose.setOnClickListener {
onClickClose()
dismiss()
}
binding.tvAuditionApply.setOnClickListener {
val phoneNumber = binding.etPhoneNumber.text.toString()
if (phoneNumber.isBlank() || phoneNumber.length != 11) {
Toast.makeText(
activity,
"잘못된 연락처 입니다.\n다시 입력해 주세요.",
Toast.LENGTH_LONG
).show()
} else if (!binding.tvAgree.isSelected) {
Toast.makeText(
activity,
"연락처 수집 및 활용에 동의하셔야 오디션 지원이 가능합니다.",
Toast.LENGTH_LONG
).show()
} else {
dismiss()
onClickApply(binding.etPhoneNumber.text.toString())
}
}
binding.tvAgree.setOnClickListener {
it.isSelected = !it.isSelected
}
binding.tvAudioFileName.text = fileName
}
}

View File

@ -4,25 +4,33 @@ import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.OpenableColumns
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.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audition.applicant.ApplicationMethodDialog
import kr.co.vividnext.sodalive.audition.applicant.AuditionApplicantListAdapter import kr.co.vividnext.sodalive.audition.applicant.AuditionApplicantListAdapter
import kr.co.vividnext.sodalive.audition.applicant.AuditionApplyDialogFragment
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.common.Constants import kr.co.vividnext.sodalive.common.Constants
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.databinding.ActivityAuditionRoleDetailBinding import kr.co.vividnext.sodalive.databinding.ActivityAuditionRoleDetailBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.write.RecordingVoiceFragment
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 AuditionRoleDetailActivity : BaseActivity<ActivityAuditionRoleDetailBinding>( class AuditionRoleDetailActivity : BaseActivity<ActivityAuditionRoleDetailBinding>(
ActivityAuditionRoleDetailBinding::inflate ActivityAuditionRoleDetailBinding::inflate
) { ), RecordingVoiceFragment.OnAudioRecordedListener {
private val viewModel: AuditionRoleDetailViewModel by inject() private val viewModel: AuditionRoleDetailViewModel by inject()
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
@ -30,6 +38,38 @@ class AuditionRoleDetailActivity : BaseActivity<ActivityAuditionRoleDetailBindin
private var auditionRoleId: Long = 0 private var auditionRoleId: Long = 0
private var isOpenInformation = false private var isOpenInformation = false
private var reApplication = false
private var isDeleteAudioFile = false
private val selectAudioActivityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == RESULT_OK) {
val fileUri = data?.data
if (fileUri != null) {
viewModel.audioFileName = getFileName(fileUri).toString()
viewModel.audioFile = File(
RealPathUtil.getRealPath(applicationContext, fileUri!!)
)
showAuditionApplyDialog()
} else {
Toast.makeText(
this,
"잘못된 녹음 파일 입니다.\n다시 선택해 주세요.",
Toast.LENGTH_SHORT
).show()
}
} else {
Toast.makeText(
this,
"잘못된 녹음 파일 입니다.\n다시 선택해 주세요.",
Toast.LENGTH_SHORT
).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
auditionRoleId = intent.getLongExtra(Constants.EXTRA_AUDITION_ROLE_ID, 0) auditionRoleId = intent.getLongExtra(Constants.EXTRA_AUDITION_ROLE_ID, 0)
@ -76,6 +116,21 @@ class AuditionRoleDetailActivity : BaseActivity<ActivityAuditionRoleDetailBindin
} }
} }
binding.tvApplicant.setOnClickListener {
if (reApplication) {
SodaDialog(
activity = this@AuditionRoleDetailActivity,
layoutInflater = layoutInflater,
title = "재지원 안내",
desc = "재지원 시 이전 지원 내역은 삭제되며 받은 투표수는 무효 처리됩니다.",
confirmButtonTitle = "확인",
confirmButtonClick = { showApplicationMethodDialog() }
).show(screenWidth)
} else {
showApplicationMethodDialog()
}
}
binding.tvSortNewest.setOnClickListener { binding.tvSortNewest.setOnClickListener {
viewModel.setSortType(AuditionRoleDetailViewModel.AuditionApplicantSortType.NEWEST) viewModel.setSortType(AuditionRoleDetailViewModel.AuditionApplicantSortType.NEWEST)
} }
@ -144,6 +199,53 @@ class AuditionRoleDetailActivity : BaseActivity<ActivityAuditionRoleDetailBindin
recyclerView.adapter = adapter recyclerView.adapter = adapter
} }
private fun showApplicationMethodDialog() {
isDeleteAudioFile = false
ApplicationMethodDialog(
activity = this@AuditionRoleDetailActivity,
layoutInflater = layoutInflater,
onClickFileUpload = {
isDeleteAudioFile = false
val intent = Intent().apply {
type = "audio/*"
action = Intent.ACTION_GET_CONTENT
addCategory(Intent.CATEGORY_OPENABLE)
}
selectAudioActivityResultLauncher.launch(
Intent.createChooser(
intent,
"Select Audio"
)
)
},
onClickRecord = {
isDeleteAudioFile = true
val fragment = RecordingVoiceFragment()
fragment.isCancelable = false
fragment.show(supportFragmentManager, fragment.tag)
}
).show(screenWidth)
}
private fun showAuditionApplyDialog() {
val auditionApplyDialogFragment = AuditionApplyDialogFragment(
fileName = viewModel.audioFileName,
onClickApply = {
viewModel.applyAudition(auditionRoleId = auditionRoleId, phoneNumber = it) {
if (isDeleteAudioFile) {
deleteAudioFile()
}
}
},
onClickClose = {
if (isDeleteAudioFile) {
deleteAudioFile()
}
}
)
auditionApplyDialogFragment.show(supportFragmentManager, auditionApplyDialogFragment.tag)
}
private fun bindData() { private fun bindData() {
viewModel.toastLiveData.observe(this) { viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
@ -177,8 +279,10 @@ class AuditionRoleDetailActivity : BaseActivity<ActivityAuditionRoleDetailBindin
} }
if (roleDetail.isAlreadyApplicant) { if (roleDetail.isAlreadyApplicant) {
reApplication = true
binding.tvApplicant.text = "오디션 재지원" binding.tvApplicant.text = "오디션 재지원"
} else { } else {
reApplication = false
binding.tvApplicant.text = "오디션 지원하기" binding.tvApplicant.text = "오디션 지원하기"
} }
} }
@ -218,4 +322,37 @@ class AuditionRoleDetailActivity : BaseActivity<ActivityAuditionRoleDetailBindin
} }
} }
} }
private fun deleteAudioFile() {
if (viewModel.audioFile != null && viewModel.audioFile!!.exists()) {
viewModel.audioFile?.delete()
viewModel.audioFileName = ""
}
}
private fun getFileName(uri: Uri): String? {
val scheme = uri.scheme
var fileName: String? = null
if (scheme == "content") {
contentResolver.query(uri, null, null, null, null)?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (nameIndex != -1 && cursor.moveToFirst()) {
fileName = cursor.getString(nameIndex)
}
}
} else if (scheme == "file") {
val file = File(uri.path ?: "")
fileName = file.name
}
return fileName
}
override fun onAudioRecorded(file: File) {
deleteAudioFile()
viewModel.audioFile = file
viewModel.audioFileName = file.name
showAuditionApplyDialog()
}
} }

View File

@ -2,16 +2,25 @@ package kr.co.vividnext.sodalive.audition.role
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.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audition.AuditionRepository import kr.co.vividnext.sodalive.audition.AuditionRepository
import kr.co.vividnext.sodalive.audition.applicant.ApplyAuditionRoleRequest
import kr.co.vividnext.sodalive.audition.applicant.GetAuditionRoleApplicantItem import kr.co.vividnext.sodalive.audition.applicant.GetAuditionRoleApplicantItem
import kr.co.vividnext.sodalive.audition.vote.VoteAuditionApplicantRequest import kr.co.vividnext.sodalive.audition.vote.VoteAuditionApplicantRequest
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okio.BufferedSink
import java.io.File
import java.util.TimeZone import java.util.TimeZone
class AuditionRoleDetailViewModel(private val repository: AuditionRepository) : BaseViewModel() { class AuditionRoleDetailViewModel(private val repository: AuditionRepository) : BaseViewModel() {
@ -41,6 +50,8 @@ class AuditionRoleDetailViewModel(private val repository: AuditionRepository) :
var page = 1 var page = 1
var isLast = false var isLast = false
var audioFile: File? = null
var audioFileName: String = ""
private var auditionRoleId = -1L private var auditionRoleId = -1L
private val pageSize = 10 private val pageSize = 10
@ -171,6 +182,78 @@ class AuditionRoleDetailViewModel(private val repository: AuditionRepository) :
) )
} }
fun applyAudition(auditionRoleId: Long, phoneNumber: String, onSuccess: () -> Unit) {
if (audioFile == null) {
_toastLiveData.value = "잘못된 녹음 파일 입니다.\n다시 선택해 주세요."
return
}
_isLoading.value = true
val request = ApplyAuditionRoleRequest(roleId = auditionRoleId, phoneNumber = phoneNumber)
val requestJson = Gson().toJson(request)
val contentFile = MultipartBody.Part.createFormData(
"contentFile",
audioFile!!.name,
body = object : RequestBody() {
override fun contentType(): MediaType {
return "audio/*".toMediaType()
}
override fun writeTo(sink: BufferedSink) {
audioFile!!.inputStream().use { inputStream ->
val buffer = ByteArray(512)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
sink.write(buffer, 0, bytesRead)
}
}
}
override fun contentLength(): Long {
return audioFile!!.length()
}
}
)
compositeDisposable.add(
repository.applyAudition(
contentFile = contentFile,
request = requestJson.toRequestBody("text/plain".toMediaType()),
token = "Bearer ${SharedPreferenceManager.token}"
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
_applicantListLiveData.value = emptyList()
page = 1
isLast = false
getAuditionRoleDetail(auditionRoleId = auditionRoleId)
onSuccess()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
)
)
}
fun setSortType(sortType: AuditionApplicantSortType) { fun setSortType(sortType: AuditionApplicantSortType) {
val prevSortType = _sortTypeLiveData.value!! val prevSortType = _sortTypeLiveData.value!!

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_13181b" />
<corners android:radius="5.3dp" />
<stroke
android:width="1dp"
android:color="@color/color_13181b" />
</shape>

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_round_corner_10_3_222222"
android:paddingVertical="13.3dp"
android:paddingHorizontal="16.7dp">
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_noti_stop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="33.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:text="오디션 지원방식"
android:textColor="@color/color_bbbbbb"
android:textSize="18.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/ll_method_buttons"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="21.3dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title">
<LinearLayout
android:id="@+id/ll_upload_file"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_5_3_13181b_3bb9f1"
android:gravity="center"
android:orientation="horizontal"
android:paddingVertical="8dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="3dp"
android:src="@drawable/ic_upload" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="파일 업로드"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_record"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_5_3_13181b_3bb9f1"
android:gravity="center"
android:orientation="horizontal"
android:paddingVertical="8dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="3dp"
android:src="@drawable/ic_mic_color_button" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="바로 녹음"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="※ 파일은 mp3, aac만 업로드 가능"
android:textColor="@color/color_777777"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_method_buttons" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_222222">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="13.3dp"
android:paddingVertical="16.7dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:fontFamily="@font/gmarket_sans_medium"
android:text="오디션 지원"
android:textColor="@color/white"
android:textSize="18.3sp" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_noti_stop" />
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="녹음파일"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@drawable/bg_round_corner_5_3_13181b"
android:orientation="horizontal"
android:paddingHorizontal="13.3dp"
android:paddingVertical="8dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_note_square" />
<TextView
android:id="@+id/tv_audio_file_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="연락처"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<EditText
android:id="@+id/et_phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@drawable/bg_round_corner_5_3_13181b"
android:fontFamily="@font/gmarket_sans_medium"
android:hint="합격시 연락받을 연락처를 남겨주세요"
android:importantForAutofill="no"
android:inputType="numberSigned"
android:maxEms="12"
android:paddingHorizontal="13.3dp"
android:paddingVertical="17dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="13.3sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor,TextFields" />
<TextView
android:id="@+id/tv_agree"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:drawablePadding="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center_vertical"
android:text="보이스온 오디오 드라마 오디션 합격시 개인 연락을 위한 개인 정보(연락처) 수집 및 활용에 동의합니다.\n오디션 지원자는 개인정보 수집 및 활용 동의에 거부할 권리가 있으며 비동의시 오디션 지원은 취소 됩니다."
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp"
app:drawableStartCompat="@drawable/ic_select" />
<TextView
android:id="@+id/tv_audition_apply"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="35dp"
android:background="@drawable/bg_round_corner_8_3bb9f1"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="13.3dp"
android:text="오디션 지원하기"
android:textColor="@color/white"
android:textSize="13.3sp" />
</LinearLayout>
</ScrollView>