Jetpack 可与 MediaRouteActionProvider 组合

Jetpack Composable with MediaRouteActionProvider

我目前正在将 Chromcast 集成到我们的应用程序中。我们正在为所有 UI 元素使用 Jetpack Compose。我们正在使用 TopAppBar 可组合项,我正在尝试使用 MediaRouteActionProvider 向其添加 Chromecast 按钮。我能找到使用 MediaRouteActionProvider 的唯一方法是使用 menu.xml 并在 onCreateOptionsMenu 中扩充菜单。

有谁知道在 Jetpack Compose 的菜单上下文之外使用 ActionProvider 的方法,还是我现在坚持使用菜单?

更新:决定再次破解此问题。

import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.mediarouter.app.MediaRouteDialogFactory
import androidx.mediarouter.media.MediaControlIntent
import androidx.mediarouter.media.MediaRouteSelector
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.cast.framework.CastStateListener

class MediaRouteViewModel(context: Context) : ViewModel(), CastStateListener {

    var isCastingAvailable = false
        private set

    val castingStateLiveData = MutableLiveData(CastingState.UNKNOWN)

    val mDialogFactory = MediaRouteDialogFactory.getDefault()

    val mSelector: MediaRouteSelector = MediaRouteSelector.Builder()
        .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
        .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
        .build()

    init {
        try {
            isCastingAvailable = true

            val castingContext = CastContext.getSharedInstance(context)
            castingContext.addCastStateListener(this)
        } catch (e: Exception) {
            // handle me please
        }
    }

    fun shouldShowChooserFragment(): Boolean {
        return when (castingStateLiveData.value) {
            CastingState.NOT_CONNECTED -> true
            CastingState.UNKNOWN -> true
            CastingState.NO_DEVICES_AVAILABLE -> true
            CastingState.CONNECTING -> false
            CastingState.CONNECTED -> false
            else -> false
        }
    }

    enum class CastingState {
        CONNECTED,
        CONNECTING,
        NOT_CONNECTED,
        NO_DEVICES_AVAILABLE,
        UNKNOWN
    }

    class Factory(val context: Context) : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return MediaRouteViewModel(context = context) as T
        }
    }

    override fun onCastStateChanged(p0: Int) {
        val castingState = when (p0) {
            CastState.CONNECTED -> CastingState.CONNECTED
            CastState.CONNECTING -> CastingState.CONNECTING
            CastState.NOT_CONNECTED -> CastingState.NOT_CONNECTED
            CastState.NO_DEVICES_AVAILABLE -> CastingState.NO_DEVICES_AVAILABLE
            else -> CastingState.UNKNOWN
        }

        castingStateLiveData.postValue(castingState)
    }
}
@Composable
fun MediaRouter(
    modifier: Modifier,
    iconWidth: Dp,
    fragmentManager: FragmentManager
) {
    val context = LocalContext.current

    val mediaRouteProviderViewModel: MediaRouteViewModel =
        viewModel(factory = MediaRouteViewModel.Factory(context))

    // Can use the casting state to change the painter accordingly
    val castingState = mediaRouteProviderViewModel.castingStateLiveData.observeAsState()

    if (mediaRouteProviderViewModel.isCastingAvailable) {
        Box(modifier = modifier.size(iconWidth)) {
            Image(
                painter = painterResource(id = R.drawable.ic_baseline_cast_connected_24),
                contentDescription = null,
                modifier = Modifier
                    .clickable {
                        val shouldShowChooserFragment = mediaRouteProviderViewModel.shouldShowChooserFragment()
                        val fragmentTag = if (shouldShowChooserFragment) "MediaRouteChooserDialogFragment" else "MediaRouteControllerDialogFragment"
                        
                        val fragment = if (shouldShowChooserFragment) {
                            mediaRouteProviderViewModel.mDialogFactory
                                .onCreateChooserDialogFragment()
                                .apply {
                                    routeSelector = mediaRouteProviderViewModel.mSelector
                                }
                        } else {
                            mediaRouteProviderViewModel.mDialogFactory.onCreateControllerDialogFragment()
                        }

                        val transaction = fragmentManager.beginTransaction()
                        transaction.add(fragment, fragmentTag)
                        transaction.commitAllowingStateLoss()
                    },
                contentScale = ContentScale.FillBounds
            )
        }
    }
}

旧 Post:

我确信在 compose 中有更好的方法来执行此操作,但这至少可以完成工作。

class MediaRouteViewModel(context: Context) : ViewModel() {
    private val mediaRouteActionProvider = MediaRouteActionProvider(context)

    val buttonView: View

    init {
        buttonView = mediaRouteActionProvider.onCreateActionView()
        mediaRouteActionProvider.routeSelector = MediaRouteSelector.Builder()
            .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
            .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
            .build()
    }


    fun onClick() {
        mediaRouteActionProvider.onPerformDefaultAction()
    }
}
@Composable
fun MediaRouter(modifier: Modifier, iconWidth: Dp) {
    val context = LocalContext.current

    val mediaRouteProviderViewModel = MediaRouteViewModel(context)

    Box(modifier = modifier.size(iconWidth)) {
        AndroidView(factory = {
            mediaRouteProviderViewModel.buttonView
        }, modifier = Modifier.fillMaxSize())

        Box(modifier = Modifier
            .fillMaxSize()
            .clickable { mediaRouteProviderViewModel.onClick() })
    }
}