Skip to main content

Maestro Panel SDK for Jetpack Compose Documentation

Overview

This SDK simplifies integrating interactive panels into your Jetpack Compose Android applications. It provides APIs for event handling, user interaction, and UI updates, enabling rich user experiences. The core composable is MaestroPanel(), which displays the panel. MaestroManager is a singleton object used for SDK initialization and management.

Installation

implementation "com.lessthan3:maestropanel:1.1.1"
// It will be updated

settings.gradle add this repo

maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/lessthan3/MaestroKit.android")
}

Key Concepts and Interfaces

The SDK uses these key interfaces:

  1. MaestroManagerInterface: For SDK initialization, configuration, and event lifecycle management. Interacted with through the MaestroManager singleton object.

  2. MaestroEventInterface: API for your app to interact with the SDK concerning the currently loaded event (e.g., clip playback, progress tracking). Implemented by MaestroEventViewModel.

  3. MaestroEventDelegate: Your app implements this to provide event-specific data and handle playback requests from the SDK.

Usage

1. Initialize the SDK

At app launch, configure the MaestroManager singleton:

MaestroManager.configure(siteId = "yourSiteId", jwt = "yourJWT")

2. Displaying the Panel and Initializing the ViewModel

To display MaestroPanel(), first initialize MaestroEventViewModel:

@Composable
fun MyEventScreen(eventId: String, viewModelStoreOwner: ViewModelStoreOwner? = null) {

val maestroEventViewModel: MaestroEventViewModel? = MaestroManager.userDidStartWatchingEvent(
eventId = eventId,
maestroEventDelegate = MaestroEventDelegateImpl(), // Your implementation
viewModelStoreOwner = viewModelStoreOwner // Optional: Activity/Fragment scope
)

// Or you can create your own viewmodel but just call
// MaestroManager.userDidStartWatchingEvent(eventId = "")

maestroEventViewModel?.let { // Use the ViewModel if successfully initialized.
MaestroPanel(maestroEventViewModel = it)
} ?: run {
// Handle the case where ViewModel initialization fails (e.g., display an error message)
Text("Error initializing Maestro Panel") // or other error handling UI

}
// ... use maestroEventViewModel to interact with the SDK (e.g., playClip, track progress)

}


class MaestroEventDelegateImpl:MaestroEventDelegate {
private var maestroKeyPlaysResponse:MutableStateFlow<MaestroResource<MaestroKeyPlaysResponse?>> = MutableStateFlow(MaestroResource.Success(FakeDataUtil.parseJsonToObjects()))

// return the maestro keyplays response
override fun keyPlaysData(eventID: String): StateFlow<MaestroResource<MaestroKeyPlaysResponse?>> {
return maestroKeyPlaysResponse
}

override fun playClip(index: Int) {
//play clip on your side
}



override fun trackAction(analytics: Map<String, String>) {

}

override fun trackImpression(analytics: Map<String, String>) {

}
}

When a user starts watching an event:

@Composable
fun MyEventScreen(, viewModelStoreOwner: ViewModelStoreOwner? = null) {

val maestroEventViewModel: MaestroEventViewModel? = MaestroManager.userDidStartWatchingEvent(
eventId = "",
maestroEventDelegate = MyMaestroEventDelegate(), // Your implementation of MaestroEventDelegate
viewModelStoreOwner = viewModelStoreOwner // Optional: for Activity/Fragment scoped ViewModels
)


// Or you can create your own viewmodel but just call
// MaestroManager.userDidStartWatchingEvent(eventId = "")

// ... use maestroEventViewModel to interact with the SDK (e.g., playClip, track progress)
}


When the user stops watching the event:

```kotlin
lifecycleScope.launch { // or viewModelScope
maestroManager.userDidStopWatchingEvent(eventId)
}

API Reference

MaestroManagerInterface


/**
This is the MaestroKit top-level API for initializing and configuring the framework. It encompasses all functionality that is not specific to an event.
*/
interface MaestroManagerInterface {
/** This configures the SDK and must be called at app launch.*/
fun configure(siteId: String, jwt: String)


/**
* Tells the SDK that the user has opened an event in the app's player.
*
* @param eventId The Event ID for the event opened in the player.
* @param maestroEventDelegate An instance of the app's implementation of [MaestroEventDelegate]`.
* @param viewModelStoreOwner (Optional) The `ViewModelStoreOwner` to be used for scoping the [MaestroEventViewModel]`.
* - If provided, the `MaestroEventViewModel` will be scoped to the given owner (e.g., an `Activity` or `Fragment`).
* - If `null`, the ViewModel will be scoped to the Composable using `viewModel()`, which works well with Jetpack Compose Navigation.
* @return An instance of `MaestroEventViewModel`, which implements `MaestroEventInterface`.
*
* After `userDidStartWatchingEvent` is called:
* - The SDK gains access to the API detailed in `MaestroEventDelegate` for making calls to the app.
* - The app gains access to the API detailed in `MaestroEventInterface` for making calls to the SDK.
*
* Or you can just use userDidStartWatchingEvent(eventId: String?) and create your own MaestroEvenViewModel
*/
@Composable
fun userDidStartWatchingEvent(
eventId: String?,
maestroEventDelegate: MaestroEventDelegate,
viewModelStoreOwner: ViewModelStoreOwner?
): MaestroEventViewModel?


/**
* Tells the SDK that the user has opened an event in the app's player.
*
* you can just create a viewmodel and pass to the maestro panel
* use [MaestroEventViewModelFactory]
* */
fun userDidStartWatchingEvent(eventId: String?)

/** Tells the SDK the user has closed an event that was open in the app's player.*/
suspend fun userDidStopWatchingEvent(eventId: String)

/** Retrieves the current Maestro Site ID.*/
suspend fun currentSiteId(): String

/** Retrieves the current authentication token.*/
suspend fun currentAuthToken(): String

/** Retrieves the currently authenticated user's ID*/
suspend fun currentUserId(): String
}```

MaestroEventInterface


/** Your app defines a delegate that is compliant with this protocol and provides a reference as part of your
initialization of the SDK.*/
interface MaestroEventDelegate {
/**
Enables the SDK to retrieve key plays data from the client app.
*/
fun keyPlaysData(eventID: String): StateFlow<MaestroResource<MaestroKeyPlaysResponse?>>

/**
Allows the SDK to request the client app play a particular key plays clip, given its array index in the key plays list.
Note that the `MaestroKeyPlaysResponse` subdivides the key plays by section, so the `index` referenced here assumes the
client app has access to a flattened list of the key plays, disregarding the sectional subdivisions.
*/
fun playClip(index: Int)

/** Allows the SDK to notify the client app of user actions for analytics tracking. Details TBD.*/
fun trackAction(analytics: Map<String, String>)

/** Allows the SDK to tell the client app that the panel should be displayed.*/
fun shouldShowPanel()

/** Allows the SDK to tell the client app that the panel should be dismissed.
As when focus is on keyplay card and user presses back we move focus to panel selector(keyplays/stats/bets)
Now when user presses back again then we send this callback to your app as now you have to catch the focus and either fullscreen the app or do something else*/

fun shouldHidePanel()

/** Allows the SDK to notify the client app of user impressions for analytics tracking. Details TBD.*/
fun trackImpression(analytics: Map<String, String>)
}

MaestroEventDelegate

/**
* This is the public API that the app can use to make calls to the SDK related to the currently-loaded event.
*/
interface MaestroEventInterface {

/**
* Tells the SDK that the user just started playing a new key play clip at the given index.
*/
fun didStartPlayingClip(index: Int)

/**
* Tells the SDK that the user just stopped playing a key play clip at the given index.
*/
fun didStopPlayingClip(index: Int)

/** Tells the SDK that the user tried to play the clip but it failed to play back.*/
fun clipDidFailToPlay(index : Int)

/**
* Updates the SDK with the latest progress in the playback of the currently-played key play clip, from 0 to 1.
*/
fun didUpdatePlaybackProgressOfClip(index: Int, progress: Float)

/**
* Tells the SDK that the app displayed the MaestroPanel.
*/
fun didShowPanel(maestroPanelType: MaestroPanelType)

/**
* Tells the SDK that the app hid the MaestroPanel.
*/
fun didHidePanel()

// MARK: - Convenience methods for retrieving SDK State

/**
* Retrieves the event ID for the currently loaded event.
*/
fun getCurrentEventID(): StateFlow<String?>

/**
* Retrieves the array index of the key play clip that is currently being played.
*/
fun getCurrentlyPlayingClipIndex(): StateFlow<Int>

/**
* Retrieves the array index of the key plays clip that was most recently played.
*/
fun getLastPlayedClipIndex(): StateFlow<Int?>

/**
* Retrieves the progress (between 0 and 1) of the currently-played key play clip.
*/
fun getCurrentClipPlaybackProgress(): StateFlow<Float>

/**
* Retrieves the number of key plays.
*/
fun getKeyPlaysCount(): StateFlow<Int>
}