GDSC HUFS 3기/Android with Kotlin Team 2

[2팀] 33 Android 뮤직 플레이어 만들기

제주도감귤쥬스 2021. 11. 29. 13:17

이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.

작성자 : 강소영

개발환경은 Windows, Android Studio입니다. 

ContentResolver를 이용해 뮤직 플레이어 만들기(음원 목록 가져오는 앱 만들기)

Intro.

ContentResolver?

Content Provider의 결과를 반환하는 브릿지 역할을 해주는 것. 컨텐트 프로바이더의 주소를 통해 데이터에 접근해 결과를 가져온다.

(Content Privider : 어플리케이션 사이에서 데이터를 공유하는 통로 역할을 함. 각종 설정값이나 DB에 접근하게 해준다.)

 

1. 음원 다운로드 받기 (외부 저장소에 접근하기)

Emulator에서 Chrome과 같은 웹 브라우저를 열어 검색창에 "free mp3 downloads last.fm"라고 검색하고 접속한다.

 

Emulator에 검색한 모습

 

↓모양 버튼을 눌러 아무 노래나 원하는 만큼 다운로드 받고 기본 메뉴로 돌아가 Play Music 앱을 실행시켜 음원이 잘 다운되었는지 확인한다.

 

이런식으로 되어있으면 다운로드 성공.

 

2. 다운로드 받은 음원 목록을 화면에 나타내는 코드 작성

외부 저장소에 저장된 음원 목록을 불러오기 위해 app->manifests에 권한을 추가하고 MainActivity에 추가한 권한을 선언해준다.

<uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE"/>
//manirests에 작성해야하는 권한을 추가하는 코드

 

val permissions = arryOf(Manifest.permission.READ_EXTERNAL_STORAGE)
//MainActivity의 Class 안에 작성해준다.

 

permissions에 권한이 있는가를 감별해내는 코드를 작성하기 위해 우선 사용자 지정 함수를 선언하고 나머지 코드를 작성해준다.

fun isPermitted() : Boolean { //imPermissed 말고 다른 이름으로 작성해도 된다.
	...
}

 

사용자 지정 함수 isPermitted()의 위쪽에 조건문을 사용해 권한 감별 코드를 작성해준다.

if(isPermitted()) {
	startProcess() //권한 있을경우 프로그램 진행
} else {
	ActivityCompat.requestPermissions( activity: this, arrayOf(permission), REQ_READ) //권한 없을경우 앱 종료. 위쪽에 val permission = Manifest.permission.READ_EXTERNAL_STORAGE와 val REQ_READ = 99도 선언해준다.
   }
 
 fun startProcess() {
 
 }//조건문 내의 코드를 밖으로 빼내서 작성해 가독성 올리기. (어쩔 수 없이 뺴낸 것도 있음. 만약 승인이 되지 않았을경우 코드를 다시 한 번 작성하는것을 방지하기 위한 용도.)

 

isPermitted()의 아래쪽에 onRequestPermissionResult 호출

override fun onRequestPermissionsResult(
	requestCode: Int,
    permissions: Arrary<out String>,
    grantResults: IntArray
    ) {
		if(requestCode == REQ_READ) { //requestCode가 맞는지 확인
        	if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            	startProcess()
            } else {
            	Toast.makeText( context: this, "권한 요청 승인시에만 앱 실행 가능", Toast.LENGTH_LONG).show() //넣으면 좋은 코드. 안넣어도 됨.
            	finish() //GRANT가 아니면 앱 종료.
                }
         }
     }

 

미리 선언해둔 isPermitted 함수의 몸체를 작성해준다.

fun isPermitted() : Boolean {
	return ContextCompat.checkSelfPermission( context: this, permission) == PackageManager.PERMISSION_GRANTED) {
}

 

3. 화면 구성하기

RecyclerView 생성 후 Constraint 연결

 

RectclerView 안에 들어가는 음원 목록을 보여줄 item 레이아웃을 생성해주고, 화면에 ImageView와 TextView를 적당히 생성/배치해준다. 보기 좋게 글자 크기를 조정해주어도 된다.

(item 레이아웃 생성 : res->layout->New->Layout Resource File)

 

나중에 헷갈리지 않도록 텍스트 id를 바꾸어준다.

ex)아티스트 이름이 들어가는 텍스트 id를 textArtist로 변경.

위에서부터 아티스트 이름, 음원 제목, 음원 길이.

 

화면을 돌려보고 깨지지 않으면 다음 단계로 넘어간다.

 

RecyclerView->Common Attributes->listitem의 맨 오른쪽에 있는 세로로 긴 조그마한 버튼을 클릭하면 미리 만들어놓은 item Layout을 불러올 수 있다.

맨 오른쪽 버튼.

 

선택하여 OK로 불러오기.

 

불러오면 화면이 이렇게 구성된다.

 

RecyclerView의 id까지 지어주면 Layout 구성이 완료된다.

4. Class 생성

contentresolve -> New -> Kotlin File/Class를 통해 노래 제목, 아티스트 이름 등 음악과 관련된 데이터를 넣을 수 있는 코틀린 클래스를 생성한다.

 

class Music(id:String, title:String?, artist:STring?, albumId:String?, duration:Long?) { //Class 이름은 마음대로 지어도 됨.
	var id:String = "" //음원 자체의 id
    var title:String? = "" //노래 제목
    var atrist:String? = "" //아티스트
    var albumId:String? = "" //앨범의 이미지(자켓사진) id
    var duration:Long? = 0 //음원 길이
} //음원 자체의 id값을 제외한 값들은 값이 없을 수 있으므로 null 처리를 해준다.

	init {
    	this.id = id //this를 통해 id가 class에 속한 변수임을 알려줌.
        this.title = title
        this.artist = artist
        this.albunId = albumId
        this.duration = duration
	}
    
    fun getMusicUri(): Uri { //음원의 URI 주소를 호출하는 함수 생성.
    	return Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_COMTENT_URI, id)
    } //이 코드를 통해 음원이 있는 주소를 반환시킨다.
   
	fun getAlbumUri(): Uri {   //앨범 URI를 가져오는 함수 생성
    	return Uri.parse( uriString: "content://content://media/external/audio/albumart/$albunId")
    } //이 코드를 통해 앨범 이미지를 반환시킨다.

 

 

위를 마치면 contentresolve -> New -> Kotlin File/Class를 통해 MusicAdapter 클래스를 생성해준다.

//Holder는 MusicAdapter Class를 구성하기 전 미리 만들어놓고 클래서 구성 후 마무리한다.
class MusicAdapter : RecyclerView.Adapter<Holder>() {

	val musicList = mutableListOf<Mustic>() //adapter가 사용한 컬렉션의 변수 정의.
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
    	val view = LayoutInflater.from(parent.content)//화면에서 사용되는 목록의 최대 개수만큼 아이템 레이아웃 생성.
 			.inflate(R.layout.item_layout, parent, attatchToRoot: false)
        return Holder(view) //view를 Holder에 담아 반환해 안드로이드가 사용하도록 부여.
 	}
    
    override fun getItemCount(): Int{
    	return musicList.size//사용할 데이터의 개수를 알려줌.
 	}
   
   	override fun onBindViewHolder(holder: Holder, position: Int) {
    	val music = musicList[position]	//화면에 보여주는 것들을 그려주는 코드
        holder.setMusic(music)
    } //Ctrl + i로 함수 구현.
}

class Holer(itemView: View) :RecyclerView.ViewHolder(itemView) {
	var musicUri:Uri? = null //현재 music URI를 저장하는 변수.

	fun setMusic(music:Music) {
    	musicUri = music.getMusicUri()
    	itemView.imageAlbum.setImageURI(music.getAlbumUri())
        itemView.textArtist.text = music.artist
        itemView.textTitle.text = music.title
        val sdf = SimpleDateFormat( pattern: "mm:ss" ) //분, 초까지만 표시
        itemView.textDuration.text = sdf.format(music.duration)
    }  
}

 

MainActivity의 startProcess에서 음원을 가져오는 코드를 작성하고, startProcess의 하단에 음원 목록을 가져오는 코드를 작성해준다.

fun startProcess() {
	
    val adapter = MusicAdapter()
    adapter.musicList.addAll(getMusicList())
    
    recyclerView.adapter = adapter //adapter를 recyclerView에 연결.
    recyclerView.layoutManager = LinearLayoutManager( context: this)
 }
    
fun getMusicList():List<Music> { //getMusicList 호출 & 컨텐트 리졸버로 음원 목록 가져오기
    val musicListUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI //데이터 테이블 주소
    //가져올 데이터 컬럼 정의
    val proj = arrayOf(
    	MediaStore.Audio.Media._ID,
        MediaStore.Audio.Media.TITLE,
        MediaStore.Audio.Media.ARTIST,
        MediaStore.Audio.Media.ALBUM_ID,
        MediaStore.Audio.Media._DURATION
    )
    //컨텐트 리졸버에 해당 데이터 요청
    val cursor = contentResolver.query(musicListUri, proj, selection: null, selectionArgs: null, setOrder: null)
    //커서로 전달받은 데이터를 꺼내서 저장
    val musicList = mutableListOf<Music>()
    while(cursor?.moveToNext() ?: false) { //null이면 while문이 작동하지 않으므로 이를 대비해 null일 경우 false임을 명시해준다.
    	val id = cursor!!.getString( columnlndex: 0)
        val title = cursor!!.getString( columnlndex: 1)
        val artist = cursor!!.getString( columnlndex: 2)
        val albumId = cursor!!.getString( columnlndex: 3)
        val duration = cursor!!.getLong( columnlndex: 4)
        
        val music = Music(id, title, artist, albumId, duration)
        musicList.add(music)
    }
    
	return musicList
}

 

코드 작성을 완료하면 Emulator로 실행해본다.

음악 소리가 잘 들리면 성공.