【Android】MediaPlayer音楽プレイヤ―にtimerを使ってcurrentPoisitionを表示してみた。

# 1.概要
　以前作った[MediaPlyer音楽プレイヤ―](https://qiita.com/guijiu/items/a6c92c7c270bc4847e21)に、再生経過時間を表すcurrentPositionを表示するように改修を行った。


<iframe width="560" height="315" src="https://www.youtube.com/embed/82FzBA_toOE?si=i-VzPIEGt_AWgqaG" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# 2.開発環境
Android Studio Ladybug | 2024.2.1 Patch 2
Build #AI-242.23339.11.2421.12550806, built on October 25, 2024
Runtime version: 21.0.3+-12282718-b509.11 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Toolkit: sun.awt.windows.WToolkit
Windows 10.0

#3 ソースコード
currentPositionの値を表示するにあたり、ポイントとなる箇所を抽出して以下のリスト１に紹介する。詳細については[MediaPlyer音楽プレイヤ―](https://qiita.com/guijiu/items/a6c92c7c270bc4847e21)や小生の[GitHub](https://github.com/taka-guijiu/MediaPlayer003)を参照して欲しい。


```Kotlin
// ーーーリスト１ーーー
class MainActivity : AppCompatActivity() {
    val mediaPlayer = MediaPlayer()    // MediaPlayerのインスタンス

    // 省略
    @RequiresApi(Build.VERSION_CODES.N)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 省略
        
        // 再生経過時間を表示する
        // 参考：https://qiita.com/kenichiro-yamato/items/161b43d152f32a3248c7
        timer("curPos", period = 1000L){
            HandlerCompat.createAsync(mainLooper).post{
                tvCurPos.text = "再生経過時間:" + convertMillisTo60(mediaPlayer.currentPosition)  // 一時停止した時にcurrentPosition(ミリ秒)を60進数に変換し表示する
            }
        }
    }

    // ミリ秒ー＞60進数変換
    // https://pisuke-code.com/android-ways-to-format-millis/
    fun convertMillisTo60(millis: Int): String? {
        val l = millis % 1000
        val s = millis / 1000 % 60
        val m = millis / 1000 / 60 % 60
        var h = millis / 1000 / (60 * 60) % 24
        h += millis / 1000 / (60 * 60 * 24) * 24
        return String.format("%d:%02d:%02d.%02d", h, m, s, l)
    }
}
```

## 3.1 timerとHandler/LooperによるcurrentPositionの取得
　再生経過時間を取得するためには、MediaPlayerクラスのcurrentPositionプロパティで取得できる。
```Kotlin
// ーーーリスト２ーーー
class MainActivity : AppCompatActivity() {
    val mediaPlayer = MediaPlayer()    // MediaPlayerのインスタンス
    override fun onCreate(savedInstanceState: Bundle?) {
        // 省略
        mediaPlayer.currentPosition
        // 省略        
    }
}
```

　但し、リスト２のようにMainActivityクラスのonCreateメソッド上で一度だけcurrentPositionを取得しても、その瞬間の値を取得するだけで、継続的な値は取得できない。
　そこで、リスト３のようにtimerとHandlerCompatを用い、定期的にcurrentPositionを取得する。
 ```Kotlin
 //ーーーリスト３ーーー
        timer(name = "curPos", period = 1000L){
            HandlerCompat.createAsync(mainLooper).post{
                tvCurPos.text = mediaPlayer.currentPosition.toString()
            }
        }
 ```
　この方法は、Qiitaの記事「[AndroidStudio:kotlinで時刻を表示する](https://qiita.com/kenichiro-yamato/items/161b43d152f32a3248c7)」や[Kotlin：時計アプリを作成](https://www.project-unknown.jp/entry/2018/10/09/015515)を参考にした。なお、これらの記事ではHanderクラスのコンストラクタを用いてオブジェクトを生成しているが、このコンストラクタがdeprecatedになったのでHandlerCompatを用いHandler/Looperによる非同期処理を実装した。HandlerCompatについては、山田著「[はじめてのAndroidアプリ開発Kotlin編](https://www.shuwasystem.co.jp/book/9784798065106.html)]の第10-02-03項(p555)を参照した。またスレッドや非同期処理については、結城著「[Java言語で学ぶデザインパターン入門マルチスレッド編](https://www.sbcr.jp/product/4797331623/)」で勉強した。第7章補講2にtimerが含まれるkotlin.concurrentパッケージのおおもとと考えられるJava.concurrentの説明が書かれている(但し、timerそのものの説明はない)。


## 3.2 ミリ秒を時分秒に変換
　currentPositionプロパティは音声ファイルの先頭からの再生時間をミリ秒の単位で出力される。これではひと目でわかりにくいため、リスト4のconvertMillisTo60関数を用いて時分秒に変換する。この関数は「[Java・Kotlinでミリ秒 ⇒ hhmmssにフォーマット変換…３つの方法](https://pisuke-code.com/android-ways-to-format-millis/)」から引用し改変した。（DateTime Apiの中などに何か素晴らしいクラスがあるのではないかとGoogleで調べていたらこのサイトの記事を見つけ、単純明快なので採用した。）

```Kotlin
// ーーーリスト４ーーー
    fun convertMillisTo60(millis: Int): String? {
        val l = millis % 1000
        val s = millis / 1000 % 60
        val m = millis / 1000 / 60 % 60
        var h = millis / 1000 / (60 * 60) % 24
        h += millis / 1000 / (60 * 60 * 24) * 24
        return String.format("%d:%02d:%02d.%02d", h, m, s, l)
```

以上、