Repository: expo/expo
File: packages/expo-audio/android/src/main/java/expo/modules/audio/AudioModule.kt (Kotlin)

Code before:
    }

    AsyncFunction("getRecordingPermissionsAsync") { promise: Promise ->
      Permissions.getPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.RECORD_AUDIO)
    }

    Function("preload") { source: AudioSource, _: Double ->
      val uri = source.uri ?: return@Function
      val upstreamFactory = httpDataSourceFactory(source.headers)
      appContext.mainQueue.launch {
        AudioPreloadCache.preload(context, uri, upstreamFactory)
      }
    }

    Function("clearPreloadedSource") { source: AudioSource ->
      val uri = source.uri ?: return@Function
      AudioPreloadCache.clearSource(context, uri)
    }

    Function("clearAllPreloadedSources") {
      AudioPreloadCache.clearAll(context)
    }

    Function("getPreloadedSources") {
      AudioPreloadCache.getPreloadedSources()
    }

    OnActivityEntersBackground {
      if (!shouldPlayInBackground) {
        releaseAudioFocus()
        players.values.forEach { player ->
          if (player.ref.isPlaying) {
            player.isPaused = true
            player.ref.pause()
          }
        }
      }
      if (!allowsBackgroundRecording) {
        recorders.values.forEach { recorder ->
          if (recorder.isRecording) {
            recorder.pauseRecording()
          }
        }
      }
    }

    OnActivityEntersForeground {
      if (!shouldPlayInBackground) {
        val hasPlayersToResume = players.values.any { it.isPaused }
        if (hasPlayersToResume) {
          requestAudioFocus()

Review comment (performance):
These functions should be async and dispatched to worker threads. For instance, `removeSpan`, which is called during `clearPreloadedSource`, is considered slow and shouldn't typically be called on the main/JS thread.

Code after:
    }

    AsyncFunction("requestRecordingPermissionsAsync") { promise: Promise ->
      Permissions.askForPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.RECORD_AUDIO)
    }

    AsyncFunction("requestNotificationPermissionsAsync") { promise: Promise ->
      Permissions.askForPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.POST_NOTIFICATIONS)
    }

    AsyncFunction("getRecordingPermissionsAsync") { promise: Promise ->
      Permissions.getPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.RECORD_AUDIO)
    }

    AsyncFunction("preload") Coroutine { source: AudioSource, _: Double ->
      val uri = source.uri ?: return@Coroutine
      val upstreamFactory = httpDataSourceFactory(source.headers)
      AudioPreloadCache.preload(context, uri, upstreamFactory)
    }

    AsyncFunction("clearPreloadedSource") Coroutine { source: AudioSource ->
      val uri = source.uri ?: return@Coroutine
      AudioPreloadCache.clearSource(context, uri)
    }

    AsyncFunction("clearAllPreloadedSources") Coroutine { ->
      AudioPreloadCache.clearAll(context)
    }

    AsyncFunction("getPreloadedSources") {
      AudioPreloadCache.getPreloadedSources()
    }

    OnActivityEntersBackground {
      if (!shouldPlayInBackground) {
        releaseAudioFocus()
        allPlayables.forEach { playable ->
          if (playable.isPlaying) {
            playable.isPaused = true
            playable.pause()
          }
        }
      }
      if (!allowsBackgroundRecording) {
        recorders.values.forEach { recorder ->
          if (recorder.isRecording) {
            recorder.pauseRecording()
          }
        }
      }
    }