feat: 커뮤니티 글쓰기/수정

- 이미지 gif 등록 기능 추가
This commit is contained in:
2025-07-03 13:15:01 +09:00
parent 6ff0d8bd61
commit e4012a1301
6 changed files with 357 additions and 85 deletions

View File

@@ -1,5 +1,40 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="false" module="true" />
<package name="android" withSubpackages="true" static="true" />
<package name="androidx" withSubpackages="true" static="true" />
<package name="com" withSubpackages="true" static="true" />
<package name="junit" withSubpackages="true" static="true" />
<package name="net" withSubpackages="true" static="true" />
<package name="org" withSubpackages="true" static="true" />
<package name="java" withSubpackages="true" static="true" />
<package name="javax" withSubpackages="true" static="true" />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="androidx" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
</value>
</option>
</JavaCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>

View File

@@ -149,6 +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.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'

View File

@@ -2,18 +2,24 @@ 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.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.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.github.dhaval2404.imagepicker.ImagePicker 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
@@ -26,6 +32,7 @@ import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityModifyBindin
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl import kr.co.vividnext.sodalive.extensions.loadUrl
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
@@ -34,8 +41,9 @@ 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 val imageResult = registerForActivityResult( private val imageCropResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { result -> ) { result ->
val resultCode = result.resultCode val resultCode = result.resultCode
@@ -45,25 +53,116 @@ class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModi
val fileUri = data?.data val fileUri = data?.data
if (fileUri != null) { if (fileUri != null) {
binding.ivContent.background = null handleCroppedImage(fileUri)
binding.ivContent.load(fileUri) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8f.dpToPx()))
}
viewModel.imageUri = fileUri
} else { } else {
Toast.makeText( showWrongImageFile()
this,
"잘못된 파일입니다.\n다시 선택해 주세요.",
Toast.LENGTH_SHORT
).show()
} }
} else if (resultCode == ImagePicker.RESULT_ERROR) { } else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show() showImagePickerError(data = data)
} }
} }
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()
@@ -89,25 +188,32 @@ class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModi
) )
} }
override fun onDestroy() {
deleteCroppedTempFile()
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)
binding.toolbar.tvBack.text = "게시글 등록" binding.toolbar.tvBack.text = "게시글 등록"
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
binding.ivPhotoPicker.setOnClickListener { binding.ivPhotoPicker.setOnClickListener { launchImagePicker() }
ImagePicker.with(this)
.crop()
.galleryOnly()
.galleryMimeTypes( // Exclude gif images
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.createIntent { imageResult.launch(it) }
}
if (SharedPreferenceManager.isAuth) { if (SharedPreferenceManager.isAuth) {
binding.llSetAdult.visibility = View.VISIBLE binding.llSetAdult.visibility = View.VISIBLE
@@ -174,11 +280,17 @@ class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModi
viewModel.imageUrlLiveData.observe(this) { viewModel.imageUrlLiveData.observe(this) {
if (!it.isNullOrBlank()) { if (!it.isNullOrBlank()) {
binding.ivContent.background = null binding.ivContent.background = null
binding.ivContent.loadUrl(it) { Glide.with(this)
crossfade(true) .load(it)
placeholder(R.drawable.ic_place_holder) .placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8f.dpToPx())) .apply(
} RequestOptions().transform(
RoundedCorners(
8f.dpToPx().toInt()
)
)
)
.into(binding.ivContent)
} }
} }

View File

@@ -2,18 +2,23 @@ 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.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.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
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 com.github.dhaval2404.imagepicker.ImagePicker 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
@@ -33,8 +38,9 @@ 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 imageResult = registerForActivityResult( private val imageCropResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { result -> ) { result ->
val resultCode = result.resultCode val resultCode = result.resultCode
@@ -44,30 +50,120 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
val fileUri = data?.data val fileUri = data?.data
if (fileUri != null) { if (fileUri != null) {
binding.ivContent.background = null handleCroppedImage(fileUri)
binding.ivContent.load(fileUri) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8f.dpToPx()))
}
viewModel.setImageUri(fileUri)
binding.llRecordAudio.visibility = View.VISIBLE
} else { } else {
binding.llRecordAudio.visibility = View.GONE showWrongImageFile()
Toast.makeText(
this,
"잘못된 파일입니다.\n다시 선택해 주세요.",
Toast.LENGTH_SHORT
).show()
} }
} else if (resultCode == ImagePicker.RESULT_ERROR) { } else if (resultCode == ImagePicker.RESULT_ERROR) {
binding.llRecordAudio.visibility = View.GONE showImagePickerError(data = data)
Toast.makeText(this, ImagePicker.getError(data), 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()
@@ -81,6 +177,9 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
override fun onDestroy() { override fun onDestroy() {
deleteAudioFile() deleteAudioFile()
deleteCroppedTempFile()
super.onDestroy() super.onDestroy()
} }
@@ -90,6 +189,19 @@ 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)
@@ -102,19 +214,7 @@ class CreatorCommunityWriteActivity : BaseActivity<ActivityCreatorCommunityWrite
fragment.show(supportFragmentManager, fragment.tag) fragment.show(supportFragmentManager, fragment.tag)
} }
binding.ivPhotoPicker.setOnClickListener { binding.ivPhotoPicker.setOnClickListener { launchImagePicker() }
ImagePicker.with(this)
.crop()
.galleryOnly()
.galleryMimeTypes( // Exclude gif images
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.createIntent { imageResult.launch(it) }
}
if (SharedPreferenceManager.isAuth) { if (SharedPreferenceManager.isAuth) {
binding.llSetAdult.visibility = View.VISIBLE binding.llSetAdult.visibility = View.VISIBLE

View File

@@ -41,20 +41,28 @@
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="121.3dp" android:layout_width="wrap_content"
android:layout_height="106.7dp" android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"> android:layout_marginTop="13.3dp">
<ImageView <ImageView
android:id="@+id/iv_content" android:id="@+id/iv_content"
android:layout_width="106.7dp" android:layout_width="wrap_content"
android:layout_height="106.7dp" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:background="@drawable/bg_round_corner_8_13181b" android:background="@drawable/bg_round_corner_8_13181b"
android:contentDescription="@null" android:contentDescription="@null"
android:maxWidth="300dp"
android:maxHeight="300dp"
android:minWidth="106.7dp"
android:minHeight="106.7dp"
android:padding="13.3dp" android:padding="13.3dp"
android:src="@drawable/ic_logo2" /> android:src="@drawable/ic_logo2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:id="@+id/iv_photo_picker" android:id="@+id/iv_photo_picker"
@@ -62,11 +70,15 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_marginStart="-20dp"
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_content"
app:layout_constraintStart_toEndOf="@+id/iv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

View File

@@ -41,20 +41,28 @@
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="121.3dp" android:layout_width="wrap_content"
android:layout_height="106.7dp" android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"> android:layout_marginTop="13.3dp">
<ImageView <ImageView
android:id="@+id/iv_content" android:id="@+id/iv_content"
android:layout_width="106.7dp" android:layout_width="wrap_content"
android:layout_height="106.7dp" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:background="@drawable/bg_round_corner_8_13181b" android:background="@drawable/bg_round_corner_8_13181b"
android:contentDescription="@null" android:contentDescription="@null"
android:maxWidth="300dp"
android:maxHeight="300dp"
android:minWidth="106.7dp"
android:minHeight="106.7dp"
android:padding="13.3dp" android:padding="13.3dp"
android:src="@drawable/ic_logo2" /> android:src="@drawable/ic_logo2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:id="@+id/iv_photo_picker" android:id="@+id/iv_photo_picker"
@@ -62,11 +70,15 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_marginStart="-20dp"
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_content"
app:layout_constraintStart_toEndOf="@+id/iv_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
@@ -98,8 +110,8 @@
android:id="@+id/ll_record_audio" android:id="@+id/ll_record_audio"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:visibility="gone"> android:visibility="gone">
@@ -129,8 +141,8 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginTop="13.3dp" android:layout_marginTop="13.3dp"
android:gravity="center_horizontal"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView