# Bunny Stream Android TV
The Bunny Stream Android TV SDK provides a seamless video streaming experience optimized for Android TV devices. This module extends the core Bunny Stream Android SDK with TV-specific features designed for the big screen.
## Table of Contents
- [Overview](#overview)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Features](#features)
- [Implementation Guide](#implementation-guide)
- [TV Controls](#tv-controls)
- [Remote Control Support](#remote-control-support)
- [Resume Position](#resume-position)
- [Settings & Configuration](#settings--configuration)
- [Error Handling](#error-handling)
- [Testing](#testing)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)
- [API Reference](#api-reference)
## Overview
The Bunny Stream TV SDK extends the core Bunny Stream Android SDK with TV-optimized components:
- **TV-Optimized Player**: Large, focusable controls designed for D-pad navigation
- **Remote Control Support**: Full support for Android TV remote controls and media keys
- **Resume Position**: Seamless resume functionality across sessions
- **Focus Management**: Proper focus handling for TV navigation
- **Settings Dialog**: TV-optimized quality, speed, and subtitle selection
- **Error Handling**: TV-appropriate error states and recovery
## Requirements
### Minimum Requirements
- Android API Level 26 (Android 8.0)
- Android TV or device with Android TV features
- Bunny Stream Android SDK (core modules)
### Dependencies
```kotlin
// TV-specific dependencies (automatically included)
implementation("androidx.leanback:leanback:1.2.0")
implementation("androidx.leanback:leanback-preference:1.2.0")
implementation("androidx.tvprovider:tvprovider:1.1.0")
```
### Supported Devices
- Android TV devices
- Android TV boxes
- Smart TVs with Android TV OS
- Fire TV devices
- Emulators with Android TV system images
## Installation
### 1. Add TV Module to Your Project
Add the TV module to your app's `build.gradle.kts`:
```kotlin
dependencies {
implementation("net.bunny:tv:latest.release")
// Core dependencies (if not already included)
implementation("net.bunny:api:latest.release")
implementation("net.bunny:player:latest.release")
}
```
### 2. Update AndroidManifest.xml
Add TV features to your main app's manifest:
```xml
```
### 3. Add TV Banner
Create a TV banner for the Android TV launcher:
- **Size**: 320x180 pixels
- **Location**: `app/src/main/res/drawable-xhdpi/tv_banner.png`
## Quick Start
### Basic TV Player Implementation
```kotlin
import net.bunny.tv.utils.isRunningOnTV
import net.bunny.tv.ui.BunnyTVPlayerActivity
class VideoActivity : AppCompatActivity() {
fun playVideo(videoId: String, libraryId: Long, videoTitle: String) {
if (isRunningOnTV()) {
// Use TV-optimized player
BunnyTVPlayerActivity.start(
context = this,
videoId = videoId,
libraryId = libraryId,
videoTitle = videoTitle
)
} else {
// Use mobile player
navigateToMobilePlayer(videoId, libraryId)
}
}
}
```
### TV Detection in MainActivity
```kotlin
import net.bunny.tv.utils.isRunningOnTV
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (isRunningOnTV()) {
setupTVInterface()
} else {
setupMobileInterface()
}
}
private fun setupTVInterface() {
// TV-specific UI setup
setContentView(R.layout.activity_main_tv)
// Configure for D-pad navigation
}
}
```
## Features
### đŽ TV-Optimized Controls
- **Large Focusable Buttons**: Designed for D-pad navigation (80dp standard, 100dp for play/pause)
- **Visual Focus Indicators**: Clear focus states with scaling animations
- **Auto-Hide Behavior**: Controls hide after 5 seconds of inactivity
- **Custom Control Layout**: Optimized for TV screen sizes and viewing distances
### đş Remote Control Support
| Key | Action |
|-----|--------|
| **D-pad Center/Enter** | Show/Hide controls or activate focused button |
| **Media Play/Pause** | Toggle playback |
| **Media Fast Forward** | Seek forward 30 seconds |
| **Media Rewind** | Seek backward 30 seconds |
| **D-pad Left/Right** | Seek when controls hidden |
| **Back** | Hide controls or exit player |
### âŻď¸ Resume Position
- **Automatic Position Saving**: Saves position every 10 seconds during playback
- **Resume Dialog**: TV-optimized dialog for resuming from saved position
- **Configurable Thresholds**: Customizable minimum watch time and resume thresholds
- **Lifecycle Integration**: Saves position on pause, stop, and app backgrounding
### âď¸ Settings & Configuration
- **TV Settings Dialog**: Remote-friendly settings interface
- **Playback Speed Control**: 0.25x to 4x speed options
- **Video Quality Selection**: Automatic and manual quality selection
- **Subtitle Management**: Enable/disable and language selection
- **Focus Navigation**: Full D-pad navigation support
## Implementation Guide
### Setting Up TV Player Activity
The `BunnyTVPlayerActivity` is the main entry point for TV video playback:
```kotlin
// Launch TV player
BunnyTVPlayerActivity.start(
context = context,
videoId = "your-video-id",
libraryId = 12345L,
videoTitle = "Your Video Title" // Optional but recommended
)
```
### TV Key Event Handling
Handle custom key events by extending the activity:
```kotlin
class CustomTVPlayerActivity : BunnyTVPlayerActivity() {
override fun handleTVKeyEvent(keyEvent: KeyEvent): Boolean {
when (keyEvent.keyCode) {
KeyEvent.KEYCODE_MENU -> {
if (keyEvent.action == KeyEvent.ACTION_DOWN) {
showCustomMenu()
return true
}
}
KeyEvent.KEYCODE_GUIDE -> {
if (keyEvent.action == KeyEvent.ACTION_DOWN) {
showProgramGuide()
return true
}
}
else -> return super.handleTVKeyEvent(keyEvent)
}
return false
}
}
```
## TV Controls
### Control Components
The TV player includes the following control components:
1. **Video Title Display** - Shows current video title
2. **Play/Pause Button** - Central play/pause control (100dp)
3. **Seek Buttons** - 30-second forward/backward seeking (80dp)
4. **Settings Button** - Opens TV settings dialog (80dp)
5. **Progress Bar** - Shows playback progress
6. **Time Display** - Current time / Total duration
### Focus Navigation
Controls are arranged for optimal D-pad navigation:
```
[Settings]
[â30s] [Play/Pause] [30sâ]
[Progress Bar]
[Time Display]
```
### Control Customization
```kotlin
// Configure control visibility and behavior
tvControls.apply {
setVideoTitle("My Video")
setOnSettingsClickListener {
showCustomSettings()
}
// Show/hide controls programmatically
show()
hide()
}
```
## Remote Control Support
### Media Key Mapping
The TV player responds to standard Android TV remote keys:
```kotlin
// Media keys handled automatically
KEYCODE_MEDIA_PLAY_PAUSE // Toggle playback
KEYCODE_MEDIA_PLAY // Start playback
KEYCODE_MEDIA_PAUSE // Pause playback
KEYCODE_MEDIA_FAST_FORWARD // Seek forward 30s
KEYCODE_MEDIA_REWIND // Seek backward 30s
```
### D-pad Navigation
```kotlin
// D-pad keys for navigation
KEYCODE_DPAD_CENTER // Activate focused element
KEYCODE_DPAD_UP/DOWN // Vertical navigation
KEYCODE_DPAD_LEFT/RIGHT // Horizontal navigation / seeking
KEYCODE_BACK // Back navigation / hide controls
```
### Custom Key Handling
```kotlin
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_CHANNEL_UP -> {
// Handle channel up
nextVideo()
return true
}
KeyEvent.KEYCODE_CHANNEL_DOWN -> {
// Handle channel down
previousVideo()
return true
}
}
return super.onKeyDown(keyCode, event)
}
```
## Resume Position
### Configuration
Configure resume position behavior:
```kotlin
val resumeConfig = ResumeConfig(
retentionDays = 7, // Keep positions for 7 days
minimumWatchTime = 30_000L, // 30 seconds minimum watch time
resumeThreshold = 0.05f, // Don't resume if < 5% watched
nearEndThreshold = 0.95f, // Don't resume if > 95% watched
enableAutoSave = true, // Auto-save every 10 seconds
saveInterval = 10_000L // Save interval in milliseconds
)
bunnyPlayer.enableResumePosition(
config = resumeConfig,
onResumePositionCallback = { position, callback ->
showResumeDialog(position, callback)
}
)
```
### Resume Dialog
The TV player shows a resume dialog when a saved position is found:
```kotlin
private fun showResumeDialog(position: PlaybackPosition, callback: (Boolean) -> Unit) {
AlertDialog.Builder(this)
.setTitle("Resume Playback")
.setMessage("Continue watching from ${formatTime(position.position)}?")
.setPositiveButton("Resume") { _, _ -> callback(true) }
.setNegativeButton("Start Over") { _, _ -> callback(false) }
.show()
}
```
## Settings & Configuration
### TV Settings Dialog
The TV settings dialog provides remote-friendly access to:
- **Playback Speed**: 0.25x, 0.5x, 0.75x, 1x, 1.25x, 1.5x, 2x
- **Video Quality**: Auto, 240p, 360p, 480p, 720p, 1080p, 4K
- **Subtitles**: Available subtitle tracks and off option
### Opening Settings
```kotlin
// Show settings from TV controls
tvControls.setOnSettingsClickListener {
val settingsDialog = TVSettingsDialog(this, bunnyPlayer)
settingsDialog.show()
}
```
## Error Handling
### Error States
The TV player handles various error conditions:
- **Network Errors**: Connection timeouts, network unavailable
- **Video Errors**: Invalid video ID, video not found, DRM errors
- **Playback Errors**: Codec issues, format not supported
### Error Display
```kotlin
// Show error with retry option
private fun showError(title: String, message: String) {
errorContainer.visibility = View.VISIBLE
errorMessage.text = message
retryButton.requestFocus() // Focus retry button for TV
}
// Handle retry
retryButton.setOnClickListener {
hideError()
loadVideo(videoId, libraryId) // Retry loading
}
```
## Testing
### TV Emulator Setup
1. **Create Android TV AVD**:
- Open AVD Manager in Android Studio
- Create new virtual device
- Select "TV" category
- Choose Android TV system image
2. **Install and Test**:
```bash
./gradlew assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apk
```
### Physical Device Testing
1. **Enable Developer Options** on Android TV
2. **Enable USB Debugging**
3. **Connect via ADB**:
```bash
adb connect
adb install app-debug.apk
```
### Testing Checklist
- [ ] App launches on TV
- [ ] Video loads and plays
- [ ] Remote control navigation works
- [ ] All buttons are focusable
- [ ] Controls auto-hide correctly
- [ ] Resume position works
- [ ] Settings dialog functions
- [ ] Error states display properly
- [ ] Performance is smooth
## Best Practices
### UI Design
- **Large Touch Targets**: Minimum 48dp for focusable elements
- **High Contrast**: Ensure good visibility from TV viewing distance
- **Clear Focus States**: Use prominent focus indicators
- **Readable Text**: Use large font sizes (16sp minimum)
### Navigation
- **Logical Focus Order**: Arrange controls in intuitive navigation flow
- **Focus Retention**: Maintain focus state during UI updates
- **Back Behavior**: Implement consistent back button behavior
- **Spatial Navigation**: Consider physical layout of controls
### Performance
- **Smooth Animations**: Keep animations under 300ms
- **Memory Management**: Release resources when not in use
- **Network Optimization**: Handle poor connectivity gracefully
- **Battery Optimization**: Avoid unnecessary background processing
## Troubleshooting
### Common Issues
#### Video Not Playing
```kotlin
// Check if BunnyStreamApi is initialized
if (!BunnyStreamApi.isInitialized()) {
BunnyStreamApi.initialize(context, accessKey, libraryId)
}
// Verify network connectivity
if (!isNetworkAvailable()) {
showNetworkError()
return
}
```
#### Controls Not Responding
```kotlin
// Ensure controls are focusable
button.isFocusable = true
button.isFocusableInTouchMode = false // Important for TV
// Check focus handling
button.onFocusChangeListener = OnFocusChangeListener { view, hasFocus ->
view.isSelected = hasFocus
}
```
## API Reference
### BunnyTVPlayerActivity
```kotlin
open class BunnyTVPlayerActivity : AppCompatActivity() {
companion object {
fun start(
context: Context,
videoId: String,
libraryId: Long,
videoTitle: String? = null,
token: String? = null,
expires: Long? = null
)
}
// Override these methods for customization
protected open fun handleTVKeyEvent(keyEvent: KeyEvent): Boolean
protected open fun showResumeDialog(position: PlaybackPosition, callback: (Boolean) -> Unit)
protected open fun showSettingsDialog()
protected open fun showError(title: String, message: String)
}
```
### TVPlayerControlsView
```kotlin
class TVPlayerControlsView : ConstraintLayout {
fun setBunnyPlayer(player: BunnyPlayer)
fun setVideoTitle(title: String)
fun setOnSettingsClickListener(listener: () -> Unit)
fun show()
fun hide()
fun showLoading()
fun hideLoading()
fun updateProgress()
fun isControlsVisible(): Boolean
}
```
### Device Detection
```kotlin
// Extension functions available
fun Context.isRunningOnTV(): Boolean
fun Context.getDeviceType(): DeviceType
// Usage
if (context.isRunningOnTV()) {
// TV-specific logic
}
```
## Support
For additional support and documentation:
- **Core SDK Documentation**: See main [README.md](../README.md)
- **Android TV Guidelines**: [Android TV Design Guidelines](https://developer.android.com/design/tv)
- **Leanback Library**: [Android Leanback Documentation](https://developer.android.com/training/tv/start)
## License
This TV SDK follows the same license as the core Bunny Stream Android SDK. See [LICENSE](../LICENSE) for details.