1. 녹음 기능 구현
private var recorder: MediaRecorder? = null
private fun onRecord(start: Boolean) = if (start) {
startRecording()
} else {
stopRecording()
}
1-1) start와 stop으로 나눠주었다.
private fun startRecording() {
state = State.RECORDING
recorder = MediaRecorder(this).apply {
setAudioSource(MediaRecorder.AudioSource.MIC) //마이크
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) //저장 방식 (3GPP:기본 원시 파일)
setOutputFile(fileName)
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //지원 형식
try {
prepare()
} catch (e: IOException) {
Log.e("App", "prepare() failed $e")
}
//이상 없으면
start()
}
binding.recordButton.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.icon_stop
)
)
binding.playButton.isEnabled = false //시작버튼 비활성화
binding.playButton.alpha = 0.3f // 시작버튼 색 변경 (초기값: 1.0)
}
private fun stopRecording() {
recorder?.apply {
stop()
release()
}
recorder = null
state = State.RELEASE
binding.recordButton.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.baseline_circle_24
)
)
binding.playButton.isEnabled = true //시작버튼 활성화
binding.playButton.alpha = 1.0f // 시작버튼 색 변경
}
3가지 상태로 나눔
private var state: State = State.RELEASE
private enum class State {
RELEASE, RECORDING, PLAYING
}
onCreate() {
...
binding.recordButton.setOnClickListener {
when (state) {
State.RELEASE -> {
record()
}
State.RECORDING -> {
onRecord(false)
}
State.PLAYING -> {}
}
}
...
}
지원형식 (Encoder)
2. 파일 저장
internal: 항상 접근 가능, 파일을 저장한 앱에서만 접근 가능하고, 앱 제거시 함께 사라진다.
external: removable한 저장소 (usb, disk 등은 항상 제거될 수 있다), 어디서든 접근 가능, 앱 제거해도 그대로 남아있다.
private var fileName: String = ""
onCreate(){
...
fileName = "${externalCacheDir?.absolutePath}/audiorecordtext.3gp"
//absolutePath 절대경로
//audiorecordtext 파일이름 뒤에 붙여질
//3gp 형식
...
3. 재생 구현
private var player: MediaPlayer? = null
binding.playButton.setOnClickListener {
when (state) {
State.RELEASE -> {
onPlay(true)
}
State.RECORDING -> { }
State.PLAYING -> {
onPlay(false)
}
}
}
private fun onPlay(start: Boolean) = if (start) startPlaying() else stopPlaying()
startPlaying과 stopPlaying 생성
private fun startPlaying() {
state = State.PLAYING
player = MediaPlayer().apply {
try {
setDataSource(fileName)
prepare()
} catch (e: IOException) {
Log.e("APP", "media player prepare fail $e")
}
start()
}
//파일 재생이 끝났을 경우
player?.setOnCompletionListener {
stopPlaying()
}
binding.recordButton.isEnabled = false
binding.recordButton.alpha = 0.3f
}
private fun stopPlaying() {
state = State.RELEASE
player?.release()
player = null
binding.recordButton.isEnabled = true
binding.recordButton.alpha = 1.0f
}
binding.playButton.setOnClickListener {
when (state) {
State.RELEASE -> {
onPlay(true)
}
else -> {
/*
State.RECORDING -> { }
State.PLAYING -> { }
*/
}
}
}
binding.stopButton.setOnClickListener {
when (state) {
State.PLAYING -> {
onPlay(false)
}
else -> {
/*
State.RELEASE -> { }
State.RECORDING -> { }
*/
}
}
}
전체코드
//MainActivity
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.Color
import android.media.MediaPlayer
import android.media.MediaRecorder
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.kotlin_recorder.databinding.ActivityMainBinding
import java.io.IOException
class MainActivity : AppCompatActivity() {
companion object {
private const val REQUEST_RECORD_AUDIO_CODE = 200
}
private lateinit var binding: ActivityMainBinding
private var recorder: MediaRecorder? = null
private var player: MediaPlayer? = null
private var fileName: String = ""
private var state: State = State.RELEASE
private enum class State {
RELEASE, RECORDING, PLAYING
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
fileName = "${externalCacheDir?.absolutePath}/audiorecordtext.3gp" //파일명
binding.recordButton.setOnClickListener {
when (state) {
State.RELEASE -> {
record()
}
State.RECORDING -> {
onRecord(false)
}
State.PLAYING -> {}
}
}
binding.playButton.setOnClickListener {
when (state) {
State.RELEASE -> {
onPlay(true)
}
else -> {
/*
State.RECORDING -> { }
State.PLAYING -> { }
*/
}
}
}
binding.stopButton.setOnClickListener {
when (state) {
State.PLAYING -> {
onPlay(false)
}
else -> {
/*
State.RELEASE -> { }
State.RECORDING -> { }
*/
}
}
}
}
private fun record() {
when {
ContextCompat.checkSelfPermission(
this, //CONTEXT -> this (activity에서의 CONTEXT는 자신이니까 this)
Manifest.permission.RECORD_AUDIO // RECORD_AUDIO로 변경
) == PackageManager.PERMISSION_GRANTED -> { //권한이 습득되어 있다면
onRecord(true)
}
ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.RECORD_AUDIO
) -> {
showPermissionRationalDialog() //AlertDialog 호출
}
else -> {
ActivityCompat.requestPermissions(
this, // CONTEXT -> this (activity에서의 CONTEXT는 자신이니까 this)
arrayOf(Manifest.permission.RECORD_AUDIO),
REQUEST_RECORD_AUDIO_CODE
)
}
}
}
private fun onRecord(start: Boolean) = if (start) startRecording() else stopRecording()
private fun onPlay(start: Boolean) = if (start) startPlaying() else stopPlaying()
private fun startRecording() {
state = State.RECORDING
recorder = MediaRecorder(this).apply {
setAudioSource(MediaRecorder.AudioSource.MIC) //마이크
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) //저장 방식 (3GPP:기본 원시 파일)
setOutputFile(fileName)
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //지원 형식
try {
prepare()
} catch (e: IOException) {
Log.e("App", "prepare() failed $e")
}
//이상 없으면
start()
}
//녹음시작 시 버튼 변경
binding.recordButton.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.icon_stop
)
)
binding.playButton.isEnabled = false //시작버튼 비활성화
binding.playButton.alpha = 0.3f // 시작버튼 색 변경 (초기값: 1.0)
}
private fun stopRecording() {
recorder?.apply { //? : null일 경우는 실행되지 않도록
stop()
release()
}
recorder = null
state = State.RELEASE
binding.recordButton.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.baseline_circle_24
)
)
binding.playButton.isEnabled = true //시작버튼 활성화
binding.playButton.alpha = 1.0f // 시작버튼 색 변경
}
private fun startPlaying() {
state = State.PLAYING
player = MediaPlayer().apply {
try {
setDataSource(fileName)
prepare()
} catch (e: IOException) {
Log.e("APP", "media player prepare fail $e")
}
start()
}
//파일 재생이 끝났을 경우
player?.setOnCompletionListener {
stopPlaying()
}
binding.recordButton.isEnabled = false
binding.recordButton.alpha = 0.3f
}
private fun stopPlaying() {
state = State.RELEASE
player?.release()
player = null
binding.recordButton.isEnabled = true
binding.recordButton.alpha = 1.0f
}
private fun showPermissionRationalDialog() {
AlertDialog.Builder(this)
.setMessage("녹음 권한을 허용하겠습니까?")
.setPositiveButton("허용") { _, _ ->
ActivityCompat.requestPermissions( //requestPermissions는 androd에서 사용해서 ActivityCompat을 붙여주어서 androidx에 있는 requestPermissions을 사용
this, // CONTEXT -> this (activity에서의 CONTEXT는 자신이니까 this)
arrayOf(Manifest.permission.RECORD_AUDIO),
REQUEST_RECORD_AUDIO_CODE
)
}
.setNegativeButton("취소") { dialogInterface, _ -> dialogInterface.cancel() }
.show()
}
private fun showPermissionSettingDialog() {
AlertDialog.Builder(this)
.setMessage("녹음 권한을 허용해야 앱을 사용 가능합니다. 앱 설정 화면으로 이동하시겠습니까?")
.setPositiveButton("권한 허용하러 가기") { _, _ ->
navigateToAppSetting()
}
.setNegativeButton("취소") { dialogInterface, _ -> dialogInterface.cancel() }
.show()
}
private fun navigateToAppSetting() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
}
startActivity(intent)
}
//허용되지 않은 경우도 설정해줘야 한다.
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
//권한이 허용되었는지 안되어있는지 체크
val audioRecordPermissionGranted = requestCode == REQUEST_RECORD_AUDIO_CODE
&& grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED
if (audioRecordPermissionGranted) {
onRecord(true)
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.RECORD_AUDIO
)
) {
showPermissionRationalDialog()
} else {
showPermissionSettingDialog()
}
}
}
}
'Android Studio > base_Project' 카테고리의 다른 글
Android 녹음기 만들기 [1/3] (UI / 권한설정) (1) | 2024.02.16 |
---|---|
[기본] 갤럭시 스톱워치 클론코딩 (2/3 기능구현) (0) | 2024.02.07 |
[기본] 갤럭시 스톱워치 클론코딩 (1/3 분석단계 및 UI ) (0) | 2024.02.06 |
[기본] 안드로이드 계산기 클론 코딩 (2/2) 완료X (1) | 2024.02.06 |
[기본] android 계산기 클론 코딩 (1/2) (0) | 2024.02.05 |