ちょっと前までは AppCompatActivity を使用した場合に PreferenceFragment が Support Library の Fragment を継承していないのでそのまま使えず歯がゆい思いをしていたが Android Support Library の Fragment を継承した PreferenceFragmentCompat が登場しその問題が解決された。 しかし古いアプリを移植しようとして RingtonePreference (通知音選択) がそのまま使えない事に気付いた。 SwitchPreferece には SwitchPreferenceCompat が用意されているのに RingtonePreference にはそういったものがない。
仕方が無いので普通の Preference で自分で実装することにした。
呼び出し側
暗黙的インテントを投げることで同機能を実装できる:
    mNotificationSound.setOnPreferenceClickListener {
        val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION)
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true)
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI)
        val existingValue = pref().getString("setting.notificationSound", null)
        // Uri として null を指定すると通知音「なし」扱いになることに注意
        val uri: Uri? = when {
            existingValue == null -> Settings.System.DEFAULT_NOTIFICATION_URI
            existingValue.length > 0 -> Uri.parse(existingValue)
            else -> null
        }
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, uri)
        startActivityForResult(intent, 0)
        true
    }
「Preference の設定がまだされていない場合」「通知音がなし (サイレント) の場合」「通知音が設定されている場合」の 3 通りあることに注意。
その場合分けは上のコードの when 文に示されている。
onActivityResult() 側
暗黙的インテントの戻り値を受け取る為に onActivityResult() も実装する:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode === 0 && data != null) {
        val ringtone = data.getParcelableExtra<Parcelable?>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
        pref().edit().putString("setting.notificationSound", ringtone?.let { it.toString() } ?: "").apply()
    } else {
        super.onActivityResult(requestCode, resultCode, data)
    }
}
こちらは通知音がなしの場合は null で渡ってくるので Preference に空文字で設定しておく。
空文字で無くても null でさえなければ何でも構わない。
null だと未設定との区別がつかなくなる。
ちなみに上記 val ringtone には content://media/internal/audio/media/22 といった感じの CONTENT_URI 表現が入ってくる。
実際に Notification を鳴らす際にこのまま使うことができる。