package com.amazonaws.ivs.basicbroadcast.viewModel import android.app.Application import android.util.Log import android.util.Size import android.widget.LinearLayout import android.widget.Toast import androidx.core.content.ContextCompat import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.amazonaws.ivs.basicbroadcast.R import com.amazonaws.ivs.basicbroadcast.common.* import com.amazonaws.ivs.broadcast.* private const val TAG = "AmazonIVS" class CustomSourceViewModel(private val context: Application) : ViewModel() { var session: BroadcastSession? = null var paused = false val preview = MutableLiveData<ImagePreviewView>() val clearPreview = MutableLiveData<Boolean>() val indicatorColor = MutableLiveData<Int>() val errorHappened = MutableLiveData<Pair<String, String>>() val disconnectHappened = MutableLiveData<Boolean>() private val broadcastListener by lazy { (object : BroadcastSession.Listener() { override fun onAnalyticsEvent(name: String, properties: String) { super.onAnalyticsEvent(name, properties) Log.d(TAG, "Analytics $name - $properties") } override fun onStateChanged(state: BroadcastSession.State) { launchMain { when (state) { BroadcastSession.State.CONNECTED -> { Log.d(TAG, "Connected state") indicatorColor.value = ContextCompat.getColor(context, R.color.colorGreen) } BroadcastSession.State.DISCONNECTED -> { Log.d(TAG, "Disconnected state") indicatorColor.value = ContextCompat.getColor(context, R.color.colorGrey) launchMain { disconnectHappened.value = !paused } } BroadcastSession.State.CONNECTING -> { Log.d(TAG, "Connecting state") indicatorColor.value = ContextCompat.getColor(context, R.color.colorYellow) } BroadcastSession.State.ERROR -> { Log.d(TAG, "Error state") indicatorColor.value = ContextCompat.getColor(context, R.color.colorRed) } BroadcastSession.State.INVALID -> { Log.d(TAG, "Invalid state") indicatorColor.value = ContextCompat.getColor(context, R.color.colorOrange) } } } } override fun onAudioStats(peak: Double, rms: Double) { super.onAudioStats(peak, rms) Log.d(TAG, "Audio stats received - peak ($peak), rms ($rms)") } override fun onDeviceRemoved(descriptor: Device.Descriptor) { super.onDeviceRemoved(descriptor) Log.d(TAG, "Device removed: ${descriptor.deviceId} - ${descriptor.type}") } override fun onDeviceAdded(descriptor: Device.Descriptor) { super.onDeviceAdded(descriptor) Log.d(TAG, "Device added: ${descriptor.urn} - ${descriptor.friendlyName} - ${descriptor.deviceId} - ${descriptor.position}") } override fun onError(error: BroadcastException) { Log.d(TAG, "Error is: ${error.detail} Error code: ${error.code} Error source: ${error.source}") error.printStackTrace() launchMain { errorHappened.value = Pair(error.code.toString(), error.detail) } } }) } /** * Create and start new session */ fun createSession(onReady: () -> Unit = {}) { session?.release() val config = BroadcastConfiguration().apply { // This slot will hold the custom audio and video. val slot = BroadcastConfiguration.Mixer.Slot.with { it.preferredVideoInput = Device.Descriptor.DeviceType.USER_IMAGE it.preferredAudioInput = Device.Descriptor.DeviceType.USER_AUDIO it.aspect = BroadcastConfiguration.AspectMode.FILL return@with it } this.mixer.slots = arrayOf(slot) this.video.size = BroadcastConfiguration.Vec2(720f, 1280f) } BroadcastSession(context, broadcastListener, config, null).apply { session = this Log.d(TAG, "Broadcast session ready: $isReady") if (isReady) { onReady() } else { Log.d(TAG, "Broadcast session not ready") Toast.makeText(context, context.getString(R.string.error_create_session), Toast.LENGTH_SHORT).show() return } } } fun endSession() { session?.stop() session?.release() session = null } /** * Display session's composite preview */ fun displayPreview() { Log.d(TAG, "Displaying composite preview") session?.let { it.awaitDeviceChanges { it.previewView.run { layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT ) clearPreview.value = true preview.value = this } } } } }