Arena Android SDK
Integrate live blog, chat, analytics, and real-time data services into your Android applications. The Arena SDK handles real-time connections, rendering, and event tracking so you can focus on your app's integration.
Table of Contents
- Requirements
- Known Compatibility Issues
- Project Setup
- Credentials
- Initialization Order
- LiveBlog
- Chat
- Analytics
- RealTimeData
- Getting Help
- License
Requirements
| Item | Requirement |
|---|---|
| Minimum SDK | API 21 |
| Compile SDK | API 34 or higher |
| Target SDK | API 34 or higher |
| Kotlin | 1.7.10 or higher |
| Java compatibility | Java 8 |
| Google Play Services | Required on device for Firebase (LiveBlog and RealTimeData) |
Known Compatibility Issues
Transitive dependencies no longer on Maven Central
The SDK has an internal dependency chain that goes through JCenter, which was shut down in 2021:
im.arena:liveblog
└── im.arena:player:1.1.0 (real version, on dead JCenter)
└── com.novoda:no-player:4.5.4 (dead JCenter)
└── com.google.android.exoplayer2:* (dead JCenter)
└── im.arena:basiccard
└── org.sufficientlysecure:html-textview:3.9 (dead JCenter)
The build will fail with errors like:
Could not find org.sufficientlysecure:html-textview:3.9
Could not find com.novoda:no-player:4.5.4
Fix: create a local stub-repo inside your project.
The approach is to intercept these unresolvable artifacts with local stubs that satisfy the compiler without pulling any further dead dependencies.
Step 1 — Create the folder structure
Create a stub-repo/ folder at the root of your project (same level as the app/ folder) with this layout:
stub-repo/
im/arena/player/1.1.0/
player-1.1.0.aar
player-1.1.0.pom
org/sufficientlysecure/html-textview/3.9/
html-textview-3.9.aar
html-textview-3.9.pom
com/novoda/no-player/4.5.4/
no-player-4.5.4.aar
no-player-4.5.4.pom
Step 2 — Create the POMs
All three POMs follow the same minimal template with no <dependencies> block — this is what breaks the dead dependency chain:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>GROUP_ID</groupId>
<artifactId>ARTIFACT_ID</artifactId>
<version>VERSION</version>
<packaging>aar</packaging>
</project>Replace GROUP_ID, ARTIFACT_ID, and VERSION for each artifact:
| File | groupId | artifactId | version |
|---|---|---|---|
player-1.1.0.pom | im.arena | player | 1.1.0 |
html-textview-3.9.pom | org.sufficientlysecure | html-textview | 3.9 |
no-player-4.5.4.pom | com.novoda | no-player | 4.5.4 |
Step 3 — Create the stub AARs
Each AAR is a ZIP file with the structure:
AndroidManifest.xml
classes.jar
R.txt (empty file)
AndroidManifest.xml (same for all three, change the package):
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="PACKAGE_NAME" />classes.jar is a JAR compiled from stub Java sources. The sources for each stub:
The im.arena:player artifact — your video extension point
im.arena:player artifact — your video extension pointim.arena.player.Player is the class the SDK instantiates to render video cards. The SDK reaches it in exactly one way (verified against the compiled BasicCard):
checkcast im/arena/player/Player
invokevirtual im/arena/player/Player.configure(LifecycleOwner, String videoUrl, String thumbnailUrl)
So any class living at im.arena.player.Player that extends a View the SDK can inflate and exposes that configure(...) method is accepted by the SDK as its own player — transparently. This is the seam you use to plug in a video player without modifying the SDK. You provide this class entirely from your own project; nothing in the SDK changes.
Implement Player on top of AndroidX Media3 (ExoPlayer), which is published on Google's Maven repository (the google() repository, already in your repositories block) and replaces the dead com.novoda:no-player → exoplayer2 chain. The SDK never knows the difference — it still just calls configure(...).
// im/arena/player/Player.java
package im.arena.player;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.OptIn;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.PlayerView;
// Several Media3 APIs (ExoPlayer, PlayerView) are still marked @UnstableApi, which is
// a @RequiresOptIn marker — opting in here is required or the build fails.
@OptIn(markerClass = UnstableApi.class)
public class Player extends ConstraintLayout implements LifecycleEventObserver {
private final PlayerView playerView;
private ExoPlayer exoPlayer;
private String currentUrl;
public Player(Context ctx) { this(ctx, null); }
public Player(Context ctx, AttributeSet attrs) { this(ctx, attrs, 0); }
public Player(Context ctx, AttributeSet attrs, int style) {
super(ctx, attrs, style);
// Inflated from XML so the PlayerView is backed by a TextureView (see the layout below).
playerView = (PlayerView) LayoutInflater.from(ctx)
.inflate(R.layout.arena_player_view, this, false);
addView(playerView, new ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
/** Called by the Arena SDK for every video card. */
public void configure(LifecycleOwner owner, String videoUrl, String thumbnailUrl) {
owner.getLifecycle().addObserver(this);
if (videoUrl == null || videoUrl.isEmpty()) return;
// The live feed rebinds the card on every refresh, calling configure() again with the
// same URL. Without this guard we would tear down and rebuild the player each time,
// interrupting playback (and possibly leaving an orphan instance still playing audio).
if (videoUrl.equals(currentUrl) && exoPlayer != null) return;
currentUrl = videoUrl;
release();
exoPlayer = new ExoPlayer.Builder(getContext()).build();
playerView.setPlayer(exoPlayer);
// Arena wraps the stream URL in a proxy (…/videoproxy?url=….m3u8), so the format
// extension is in the query string and ExoPlayer can't infer it from the path.
// Set the MIME type explicitly so HLS/DASH route to the right MediaSource.
MediaItem.Builder item = new MediaItem.Builder().setUri(videoUrl);
if (videoUrl.contains(".m3u8")) {
item.setMimeType(MimeTypes.APPLICATION_M3U8);
} else if (videoUrl.contains(".mpd")) {
item.setMimeType(MimeTypes.APPLICATION_MPD);
}
exoPlayer.setMediaItem(item.build());
exoPlayer.setPlayWhenReady(false); // tap to play; set true to autoplay
exoPlayer.prepare();
}
public void stop() { release(); }
// LifecycleEventObserver has a single abstract method, so it works regardless of which
// androidx.lifecycle version the SDK resolves at runtime. DefaultLifecycleObserver's
// empty default methods live in lifecycle-common-java8 and can throw AbstractMethodError.
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_STOP) {
if (exoPlayer != null) exoPlayer.pause();
} else if (event == Lifecycle.Event.ON_DESTROY) {
release();
}
}
private void release() {
if (exoPlayer != null) { exoPlayer.release(); exoPlayer = null; }
playerView.setPlayer(null);
}
}The PlayerView is inflated from a layout so it can use a TextureView instead of the default SurfaceView. The live blog is a RecyclerView that detaches, reattaches and re-lays-out the card on every feed refresh; a SurfaceView loses its surface in that flow — the video freezes on the last frame while audio keeps playing — whereas a TextureView survives it. Add this layout to your project (e.g. res/layout/arena_player_view.xml):
<?xml version="1.0" encoding="utf-8"?>
<androidx.media3.ui.PlayerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:surface_type="texture_view"
app:resize_mode="fit" />Video size. The SDK's video card fixes the player area to a short, full-width band (≈16:9). Vertical (portrait) videos are letterbox-fitted into it and look small — this height is set by the SDK card layout and cannot be changed from your
Playerclass (confirmed:setMinimumHeighthas no effect). Your only lever isapp:resize_mode:fit(default, whole video visible) orzoom(fills the width but crops portrait videos top/bottom).
Because this Player references Media3, declare Media3 in your app-level build.gradle. It resolves from the google() repository, so the stub AAR's POM stays dependency-free (the dead chain remains broken):
dependencies {
implementation 'androidx.media3:media3-exoplayer:1.4.1' // or newer
implementation 'androidx.media3:media3-ui:1.4.1'
implementation 'androidx.media3:media3-exoplayer-hls:1.4.1' // Arena videos are HLS (.m3u8)
// implementation 'androidx.media3:media3-exoplayer-dash:1.4.1' // add if your feed serves DASH (.mpd)
}Without
media3-exoplayer-hlsthe player fails withUnrecognizedInputFormatException/Source errorand the video silently stops — ExoPlayer only handles progressive formats (MP4, etc.) out of the box.
Don't display video cards at all? Leave the body of
configure()empty (and drop the Media3 imports/dependencies). The same class then just satisfies the build without playing anything.
Building this Player into the AAR with raw javac is impractical (it needs the Media3, ConstraintLayout, and Lifecycle AARs on the classpath). Use a throwaway Android library module that compiles to an AAR you then copy into stub-repo:
- Create a minimal library module (e.g.
arena-player/) with thePlayer.javaabove undersrc/main/java/im/arena/player/, thearena_player_view.xmllayout undersrc/main/res/layout/,minSdk 21, and the Media3 + ConstraintLayout + Lifecycle dependencies. - Build it:
./gradlew :arena-player:assembleRelease. - Copy
arena-player/build/outputs/aar/arena-player-release.aartostub-repo/im/arena/player/1.1.0/player-1.1.0.aar.
Prefer to keep the player as a live module instead of copying an AAR? Use Gradle dependency substitution:
configurations.all { resolutionStrategy.dependencySubstitution { substitute module('im.arena:player') using project(':arena-player') } }Same result, no AAR copy step — and still nothing changes in the SDK.
org.sufficientlysecure:html-textview — BasicCard calls HtmlFormatter.formatHtml(). The stub delegates to android.text.Html:
// org/sufficientlysecure/htmltextview/HtmlFormatterBuilder.java
package org.sufficientlysecure.htmltextview;
public class HtmlFormatterBuilder {
private String html = "";
public HtmlFormatterBuilder setHtml(String html) {
this.html = html != null ? html : "";
return this;
}
public String getHtml() { return html; }
}// org/sufficientlysecure/htmltextview/HtmlFormatter.java
package org.sufficientlysecure.htmltextview;
import android.os.Build;
import android.text.Html;
import android.text.Spanned;
public class HtmlFormatter {
public static Spanned formatHtml(HtmlFormatterBuilder builder) {
String html = builder.getHtml();
if (html == null || html.isEmpty())
return new android.text.SpannableString("");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
else
return Html.fromHtml(html);
}
}Compile with android.jar only:
javac -cp android.jar -source 8 -target 8 \
org/sufficientlysecure/htmltextview/*.java
jar cf classes.jar org/sufficientlysecure/htmltextview/*.classcom.novoda:no-player — neither Player option above references NoPlayer (Option 1 is a no-op; Option 2 uses Media3), and the player POM declares no dependencies, so this artifact is resolved but never actually loaded at runtime. An empty classes.jar is enough:
jar cf classes.jar # empty JARStep 4 — Register the stub-repo in build.gradle
In your project-level build.gradle, add stub-repo first in the repositories list so it takes priority over Maven Central:
allprojects {
repositories {
maven { url "$rootDir/stub-repo" } // must be first
google()
mavenCentral()
}
}After syncing, the build will resolve all three dead artifacts from the local repo and skip any further transitive resolution for them.
Android 14+ crash in LiveBlog and RealTimeData (gRPC TLS reflection)
The SDK uses Firebase Firestore 21.2.1 with gRPC 1.21.0. On Android 14 (API 34) and higher, this version of gRPC attempts to configure TLS via reflection on a restricted system API (getAlpnSelectedProtocol), which crashes the app with:
java.lang.AssertionError: Method getAlpnSelectedProtocol not supported
at io.grpc.okhttp.OkHttpProtocolNegotiator$AndroidNegotiator.negotiate(...)
Fix: add the Conscrypt security provider before initializing any Arena SDK:
- Add the dependency:
dependencies {
implementation 'org.conscrypt:conscrypt-android:2.5.2'
}- Install Conscrypt as the first security provider in
Application.onCreate(), before any Arena SDK call:
import org.conscrypt.Conscrypt
import java.security.Security
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Security.insertProviderAt(Conscrypt.newProvider(), 1)
// Arena SDK initialization follows...
}
}This replaces the system TLS stack with Conscrypt, which exposes ALPN via proper APIs that gRPC can use without reflection.
Project Setup
These steps must be completed once before adding any Arena SDK module to your project.
Step 1 — Add Maven Central to your repositories
Open your project-level build.gradle (the one at the root of your project, not inside app/) and make sure mavenCentral() is listed under allprojects.repositories:
allprojects {
repositories {
google()
mavenCentral()
}
}If your project uses
dependencyResolutionManagementinsettings.gradleinstead (newer project templates), add it there:dependencyResolutionManagement { repositories { google() mavenCentral() } }
Step 2 — Add Google Services plugin
The Arena Analytics and RealTimeData SDKs depend on Firebase internally. Add the Google Services plugin to your project-level build.gradle:
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.1'
}
}Then apply it at the top of your app-level build.gradle (app/build.gradle):
apply plugin: 'com.google.gms.google-services'Step 3 — Add google-services.json
google-services.json- Go to the Firebase Console and open (or create) your project.
- Register your Android app with your app's package name.
- Download the
google-services.jsonfile and place it inside theapp/folder of your project.
Never done this before? See the step-by-step guide with screenshots: How to create a Firebase project and obtain google-services.json.
MyApp/
app/
google-services.json ← here
src/
build.gradle
build.gradle
Step 4 — Add Internet permission
Open app/src/main/AndroidManifest.xml and add the Internet permission before the <application> tag:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application ...>
...
</application>
</manifest>Step 5 — Create an Application class
All Arena SDKs must be initialized inside Application.onCreate(). If your project does not have an Application subclass yet, create one:
// app/src/main/java/com/example/myapp/MyApplication.kt
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// SDK initialization goes here (see each module's section below)
}
}Then register it in AndroidManifest.xml via the android:name attribute on <application>:
<application
android:name=".MyApplication"
android:label="@string/app_name"
...>Credentials
| Module | Credential | Where to obtain |
|---|---|---|
| LiveBlog | publisherSlug, eventSlug | Your Arena account manager |
| Chat | writeKey (site slug), slug (chat room slug) | Arena Dashboard → Settings. The chat room slug is the last path segment in the chat list URL. |
| Analytics | writeKey | Your Arena account manager |
| RealTimeData | publisherSlug, eventSlug | Your Arena account manager |
Initialization Order
Important: when using LiveBlog,
RealTimeData.configure()must be called beforeLiveBlog.configure(). LiveBlog depends on the RealTimeData connection being established first.
Modules used independently (Analytics, Chat, or RealTimeData alone) do not require any specific ordering.
Note: LiveBlog internally calls
Analytics.configure()on first data load, even if you do not declare theim.arena:analyticsdependency explicitly in yourbuild.gradle. You will see analytics-related log entries as a result — this is expected behavior.
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Required on Android 14+ — install before any Arena SDK call
Security.insertProviderAt(Conscrypt.newProvider(), 1)
// 1. RealTimeData FIRST (required when using LiveBlog)
RealTimeData
.logLevel(LogLevel.NONE)
.configure(this, Environment.PRODUCTION)
// 2. LiveBlog SECOND
LiveBlog
.environment(Environment.PRODUCTION)
.logLevel(LogLevel.NONE)
.configure(this, "your-publisher-slug", "your-event-slug")
// Analytics and Chat can be initialized in any order
Analytics
.environment(Environment.PRODUCTION)
.logLevel(LogLevel.NONE)
.configure(this, "your-write-key")
}
}LiveBlog
Displays a real-time live blog feed. Supports the following card types: title, description, publisher, pinned, summary, player/person, article, social, video, golf, and live score.
Video cards: the SDK delegates playback to a
im.arena.player.Playerclass that you provide. To actually play video (rather than just satisfy the build), implement it with AndroidX Media3 — see Option 2 in the player section.
Step 1 — Add the dependency
In your app-level build.gradle:
dependencies {
implementation 'im.arena:liveblog:1.30.0'
}Step 2 — Add ProGuard rules
If your app uses minifyEnabled true, add to your proguard-rules.pro:
-keep class im.arena.liveblog.** { *; }
-keep class im.arena.analytics.** { *; }
-keep class im.arena.commons.** { *; }
-keep class im.arena.realtimedata.** { *; }
Step 3 — Initialize in Application
In MyApplication.onCreate(), configure RealTimeData first, then LiveBlog (see Initialization Order):
RealTimeData
.logLevel(LogLevel.NONE)
.configure(this, Environment.PRODUCTION)
LiveBlog
.environment(Environment.PRODUCTION)
.logLevel(LogLevel.NONE)
.configure(this, "your-publisher-slug", "your-event-slug")Step 4 — Fix manifest theme conflict
The LiveBlog SDK declares its own android:theme in its internal manifest. This conflicts with your app's theme declaration during manifest merging, causing a build error. Add tools:replace="android:theme" to the <application> tag in AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:theme="@style/Theme.YourApp"
tools:replace="android:theme"
...>Without this, the build fails with:
Attribute application@theme value=(@style/Theme.YourApp) from AndroidManifest.xml
is also present in [im.arena:liveblog] value=(@style/...)
Step 5 — Add the view to your layout
In your Activity or Fragment layout XML file:
<im.arena.liveblog.LiveBlog
android:id="@+id/live_blog"
android:layout_width="match_parent"
android:layout_height="match_parent" />Step 6 — Start the live blog in your Activity
In Activity.onCreate(), after inflating the layout, call start() on the view reference:
class MyActivity : AppCompatActivity() {
// Using ViewBinding (recommended)
private lateinit var binding: ActivityMyBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMyBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.liveBlog.start()
}
}If you are not using ViewBinding, get the reference with
findViewById:val liveBlog = findViewById<im.arena.liveblog.LiveBlog>(R.id.live_blog) liveBlog.start()
Optional — Handle item clicks
Implement LiveBlogCallback on your Activity or Fragment to receive click events. The full import path is im.arena.liveblog.commons.LiveBlogCallback:
import im.arena.liveblog.commons.LiveBlogCallback
class MyActivity : AppCompatActivity(), LiveBlogCallback {
override fun onItemClick(view: View?, position: Int) {
// A regular card was tapped
}
override fun onPredictionItemClick(view: View?, position: Int) {
// A prediction card was tapped
}
override fun onLiveScoreItemClick(view: View?, position: Int) {
// A live score card was tapped
}
override fun onItemClick(view: View?, positionParent: Int, positionChild: Int) {
// A nested item inside a card was tapped
}
}Chat
A ready-to-use live group chat embedded as an Android Fragment. Supports anonymous users and SSO-authenticated users.
Note: The Chat SDK uses Jetpack Compose internally. If your project does not already include Compose, the transitive dependencies pulled by the SDK are sufficient — no extra Compose setup is needed in most cases. If you see build errors related to Compose, ensure your
compileSdkVersionis at least 31 and that you havekotlin-gradle-plugin1.7.10 or higher.
Step 1 — Add the dependency
dependencies {
implementation 'im.arena:chat:1.4.0'
}Step 2 — Add ProGuard rules
-keep class im.arena.chat.** { *; }
-keep class im.arena.analytics.** { *; }
-keep class im.arena.commons.** { *; }
-keep class im.arena.realtimedata.** { *; }
Step 3 — Observe SSO events (before embedding the fragment)
Set up the event observer in Activity.onCreate() before the fragment transaction so it is ready to fire immediately:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
// 1. Observe FIRST
Chat.liveDataEvents().observe(this) { event ->
when (event) {
Events.SSO_REQUIRED -> {
// The user tapped login — launch your own login flow here.
// After login succeeds, call Chat.loggedUser(...) (see SSO section below).
}
else -> {}
}
}
// 2. Then embed the fragment
embedChatFragment()
}
}Step 4 — Configure and embed the fragment
private fun embedChatFragment() {
val chatFragment = Chat
.environment(Environment.PRODUCTION)
.logLevel(LogLevel.NONE)
.configure(application, "your-write-key", "your-chat-slug")
.newInstance()
supportFragmentManager
.beginTransaction()
.replace(R.id.container, chatFragment)
.commitAllowingStateLoss()
}Your layout must have a container view with the matching id:
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />Step 5 — SSO (optional)
To start the chat with a pre-authenticated user, call Chat.loggedUser() at any point before or after embedding the fragment:
Chat.loggedUser(
ExternalUser(
id = "user-123",
name = "John Doe",
email = "[email protected]",
image = "https://example.com/avatar.jpg",
firstName = "John",
lastName = "Doe"
)
)Analytics
Tracks user actions, screen views, and identity to power the Arena analytics pipeline.
Step 1 — Add the dependency
dependencies {
implementation 'im.arena:analytics:1.21.1'
}Step 2 — Add ProGuard rules
-keep class im.arena.analytics.** { *; }
-keep class im.arena.commons.** { *; }
-keep class com.google.firebase.iid.** { *; }
Step 3 — Initialize in Application
Analytics
.environment(Environment.PRODUCTION)
.logLevel(LogLevel.NONE)
.configure(this, "your-write-key")Track — record a user action
Analytics.instance().track(
"Post Reacted",
hashMapOf(
"postId" to "WOR06DvfpcRaylQJoZe",
"reaction" to "thumbs_up"
)
)Page — record a screen view
Analytics.instance().page(
"Home",
hashMapOf("referrer" to "push_notification")
)Identify — associate a user with their events
Call this after login, registration, or profile update:
Analytics.instance().identify(
"user-43564",
hashMapOf(
"name" to "John Doe",
"email" to "[email protected]",
"plan" to "business"
)
)RealTimeData
Provides raw real-time data streams via Firebase Firestore, giving you full control over rendering. The SDK uses RxJava 3, which is included as a transitive dependency — you do not need to declare it separately.
Note: If you need to observe results on the Android main thread (e.g., to update a RecyclerView), you must add
rxandroidexplicitly:implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'Then use
.observeOn(AndroidSchedulers.mainThread())before.subscribe().
Step 1 — Add the dependency
dependencies {
implementation 'im.arena:realtimedata:1.40.0'
}Step 2 — Add ProGuard rules
-keep class im.arena.realtimedata.** { *; }
Step 3 — Initialize in Application
Note: unlike other modules, the
Environmentis passed as a parameter toconfigure(), not via a builder method.
RealTimeData
.logLevel(LogLevel.NONE)
.configure(this, Environment.PRODUCTION)Step 4 — Load cached metadata, then subscribe to the stream
The typical flow is:
- Call
cachedRepository.live()to load the event metadata and get theeventId. - Use the
eventIdto subscribe to the real-time play-by-play stream. - Clean up subscriptions in
onDestroy().
class MyActivity : AppCompatActivity() {
private val compositeDisposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadEvent()
}
private fun loadEvent() {
RealTimeData
.instance()
.cachedRepository
.live("your-publisher-slug", "your-event-slug")
.subscribe(
{ eventData -> subscribeToStream(eventData.eventInfo?.key) },
{ error -> /* handle error */ }
)
.also { compositeDisposable.add(it) }
}
private fun subscribeToStream(eventId: String?) {
RealTimeData
.instance()
.playByPlay
.realtime(
eventId = eventId,
orderBy = OrderBy.NEWEST
)
.subscribe(
{ posts -> /* update your RecyclerView adapter with the List<Posts> */ },
{ error -> /* handle error */ }
)
.also { compositeDisposable.add(it) }
}
override fun onDestroy() {
compositeDisposable.clear() // cancels all Firestore listeners automatically
super.onDestroy()
}
}realtime() full signature
realtime() full signatureRealTimeData.instance().playByPlay.realtime(
eventId = "your-event-id", // obtained from cachedRepository.live()
orderBy = OrderBy.NEWEST, // OrderBy.NEWEST or OrderBy.OLDEST
priority = Priority.DESCENDING, // optional, default: DESCENDING
perPage = 20 // optional, default: 20
)Returns Flowable<List<Posts>>.
Getting Help
Issues are tracked on GitHub Issues.
| Label | Meaning |
|---|---|
bug | Feature that should work but doesn't |
enhancement | Minor tweak to existing behavior |
feature | New behavior |
question | Usage question — no SDK change needed |
duplicate | Already tracked elsewhere |
wontfix | Won't be fixed due to compatibility or design |
non-library | Documentation, samples, or build process |
License
Arena Android SDK is proprietary software. All rights reserved. See the LICENSE file for details.
Copyright © 2022 Arena Im.