Coverage Summary for Class: SoundChooserImpl (com.vsevolodganin.clicktrack.soundlibrary)
Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
SoundChooserImpl |
0%
(0/6)
|
7.1%
(1/14)
|
0%
(0/24)
|
0%
(0/167)
|
SoundChooserImpl$getInitialUri$1 |
|
SoundChooserImpl$launchFor$1 |
|
SoundChooserImpl$OpenAudio |
0%
(0/4)
|
0%
(0/10)
|
0%
(0/9)
|
0%
(0/53)
|
SoundChooserImpl$OpenAudioRequest |
0%
(0/3)
|
0%
(0/2)
|
0%
(0/5)
|
0%
(0/66)
|
SoundChooserImpl$OpenAudioRequest$Companion |
0%
(0/1)
|
|
0%
(0/1)
|
0%
(0/4)
|
Total |
0%
(0/14)
|
3.8%
(1/26)
|
0%
(0/39)
|
0%
(0/290)
|
package com.vsevolodganin.clicktrack.soundlibrary
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.DocumentsContract
import androidx.activity.result.contract.ActivityResultContract
import androidx.appcompat.app.AppCompatActivity
import com.arkivanov.essenty.statekeeper.StateKeeperOwner
import com.vsevolodganin.clicktrack.di.component.MainControllerScope
import com.vsevolodganin.clicktrack.model.ClickSoundSource
import com.vsevolodganin.clicktrack.model.ClickSoundType
import com.vsevolodganin.clicktrack.model.ClickSoundsId
import com.vsevolodganin.clicktrack.storage.ClickSoundsRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer
import me.tatarka.inject.annotations.Inject
@MainControllerScope
@Inject
class SoundChooserImpl(
private val activity: AppCompatActivity,
private val clickSoundsRepository: ClickSoundsRepository,
stateKeeperOwner: StateKeeperOwner,
) : SoundChooser {
private val pendingRequestState = MutableStateFlow<OpenAudioRequest?>(
stateKeeperOwner.stateKeeper.consume(OpenAudioRequest.SAVED_STATE_KEY, serializer()),
)
init {
stateKeeperOwner.stateKeeper.register(OpenAudioRequest.SAVED_STATE_KEY, serializer()) {
pendingRequestState.value
}
}
private val launcher = activity.registerForActivityResult(OpenAudio()) { uri ->
val request = pendingRequestState.value
if (uri != null && request != null) {
obtainPersistentPermissions(uri)
pendingRequestState.value = null
clickSoundsRepository.update(request.id, request.type, ClickSoundSource.Uri(uri.toString()))
}
}
override suspend fun launchFor(id: ClickSoundsId.Database, type: ClickSoundType) {
val initialUri = getInitialUri(id, type)
pendingRequestState.value = OpenAudioRequest(id, type)
launcher.launch(initialUri)
}
private suspend fun getInitialUri(id: ClickSoundsId, type: ClickSoundType): Uri? {
return when (id) {
is ClickSoundsId.Builtin -> null
is ClickSoundsId.Database -> clickSoundsRepository.getById(id).firstOrNull()?.value?.beatByType(type)?.value
}?.let(Uri::parse)
}
private fun obtainPersistentPermissions(uri: Uri) {
val contentResolver = activity.contentResolver
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
@Serializable
private class OpenAudioRequest(
val id: ClickSoundsId.Database,
val type: ClickSoundType,
) {
companion object {
const val SAVED_STATE_KEY = "open_audio_request"
}
}
private class OpenAudio : ActivityResultContract<Uri?, Uri?>() {
override fun createIntent(context: Context, input: Uri?): Intent {
return Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "audio/*"
addCategory(Intent.CATEGORY_OPENABLE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
putExtra(DocumentsContract.EXTRA_INITIAL_URI, input)
}
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
return intent?.takeIf { resultCode == Activity.RESULT_OK }?.data
}
override fun getSynchronousResult(context: Context, input: Uri?): SynchronousResult<Uri?>? = null
}
}