# How to Add Custom Player Actions in SmartTube: A Complete Developer Guide

> Learn how to add custom player actions in SmartTube. Explore Player Actions handled by VideoPlayerGlue and implement logic via the PlaybackPresenter.

- Repository: [Yuriy L/SmartTube](https://github.com/yuliskov/SmartTube)
- Tags: how-to-guide
- Published: 2026-09-14

---

**SmartTube Player Actions are user interactions—from basic playback controls to custom buttons—that are handled by the `VideoPlayerGlue` class and routed through the `PlayerActionListener` interface in [`PlaybackFragment.java`](https://github.com/yuliskov/SmartTube/blob/main/PlaybackFragment.java) to execute logic via the `PlaybackPresenter`.**

SmartTube is an advanced open-source YouTube client engineered for Android TV and set-top boxes. For developers extending the default playback interface with functionality like "Subscribe," "Download," or "Like" buttons, understanding the Player Actions architecture is essential. This guide examines how these actions are structured in the `yuliskov/SmartTube` repository and provides a complete implementation path for adding new controls.

## What Are Player Actions in SmartTube?

Player Actions represent every interactive element within the playback overlay, ranging from standard transport controls to application-specific feature buttons. The architecture separates the **UI presentation layer** (managed by `VideoPlayerGlue`) from the **business logic layer** (handled by `PlaybackPresenter`), connected through a strict listener contract defined in `VideoPlayerGlue.OnActionClickedListener`.

The concrete implementation of this listener resides in [`PlaybackFragment.java`](https://github.com/yuliskov/SmartTube/blob/main/PlaybackFragment.java) as the inner class `PlayerActionListener` (lines 90–118). This class intercepts all user inputs—including clicks, long-presses, and key events—and delegates them to the presenter for execution.

### Core Action Types

The `PlayerActionListener` defines distinct handler methods for different interaction patterns:

- **Transport Controls**: `onPrevious()`, `onNext()`, `onPlay()`, and `onPause()` manage standard media navigation
- **Custom Buttons**: `onAction(int actionId, int actionIndex)` handles short-clicks on UI buttons defined by integer IDs
- **Long-Press Gestures**: `onLongAction(int actionId, int actionIndex)` triggers alternative functionality when users hold a button
- **Focus Navigation**: `onTopEdgeFocused()` manages overlay visibility when navigating to the top of the player
- **Hardware Keys**: `onKeyDown(int keyCode)` captures remote control button presses for custom shortcuts

## How the Player Action Architecture Works

The event flow follows a unidirectional pattern from the UI layer to the data layer. In [`VideoPlayerGlue.java`](https://github.com/yuliskov/SmartTube/blob/main/VideoPlayerGlue.java) (located at [`smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/playback/other/VideoPlayerGlue.java`](https://github.com/yuliskov/SmartTube/blob/main/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/playback/other/VideoPlayerGlue.java)), the glue class constructs the `PlaybackControlsRow` and defines the `OnActionClickedListener` interface.

When a user interacts with any control, `VideoPlayerGlue` invokes the corresponding method on the registered listener. The `PlayerActionListener` instance (implemented in [`PlaybackFragment.java`](https://github.com/yuliskov/SmartTube/blob/main/PlaybackFragment.java) at [`smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/playback/PlaybackFragment.java`](https://github.com/yuliskov/SmartTube/blob/main/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/playback/PlaybackFragment.java)) receives these callbacks and immediately forwards them to `mPlaybackPresenter`.

For example, when the "Next" button is clicked, the execution chain is:
1. `VideoPlayerGlue` detects the click
2. `PlayerActionListener.onNext()` is invoked
3. Method calls `mPlaybackPresenter.onNextClicked()` to load the subsequent video

## How to Add a New Custom Player Action

Extending the player interface requires modifications across the UI glue, the fragment listener, and resource files. Follow these implementation steps to inject a custom button into the playback controls.

### Step 1: Define a Unique Action ID

Create a constant identifier in your `VideoPlayerGlue` subclass or companion object to prevent collisions with existing actions (which typically use IDs below 1000).

```java
public class CustomVideoPlayerGlue extends VideoPlayerGlue {
    private static final int ACTION_LIKE = 1001;
    private static final int ACTION_DOWNLOAD = 1002;
}

```

### Step 2: Create the UI Button

Instantiate a `PlaybackControlsRow.Action` within the method that builds your control row, typically inside `VideoPlayerGlue` or a subclass constructor. You must provide the action ID, a label string, and a drawable icon.

```java
Drawable likeIcon = context.getResources().getDrawable(R.drawable.ic_like, null);
PlaybackControlsRow.Action likeAction = new PlaybackControlsRow.Action(
    ACTION_LIKE, 
    context.getString(R.string.action_like), 
    likeIcon
);
controlsRow.addAction(likeAction);

```

Store the string resource in [`smarttubetv/src/main/res/values/strings.xml`](https://github.com/yuliskov/SmartTube/blob/main/smarttubetv/src/main/res/values/strings.xml) and the vector drawable in [`smarttubetv/src/main/res/drawable/ic_like.xml`](https://github.com/yuliskov/SmartTube/blob/main/smarttubetv/src/main/res/drawable/ic_like.xml).

### Step 3: Handle the Click Event

Extend the `PlayerActionListener` inner class in [`PlaybackFragment.java`](https://github.com/yuliskov/SmartTube/blob/main/PlaybackFragment.java) to intercept your custom action ID within the `onAction` method.

```java
@Override
public void onAction(int actionId, int actionIndex) {
    if (actionId == ACTION_LIKE) {
        mPlaybackPresenter.toggleLikeStatus();
    } else if (actionId == ACTION_DOWNLOAD) {
        mPlaybackPresenter.startDownload();
    } else {
        // Delegate to default handler for standard actions
        mPlaybackPresenter.onButtonClicked(actionId, actionIndex);
    }
}

```

### Step 4: Handle Long-Press Events (Optional)

For secondary functionality—such as opening a settings menu—implement the long-press handler in the same listener class.

```java
@Override
public void onLongAction(int actionId, int actionIndex) {
    if (actionId == ACTION_LIKE) {
        mPlaybackPresenter.showLikeOptions();
    } else {
        mPlaybackPresenter.onButtonLongClicked(actionId, actionIndex);
    }
}

```

### Step 5: Update UI Resources

Ensure all visual assets meet Android TV Leanback specifications:

1. Add vector drawables at `smarttubetv/src/main/res/drawable/` using 24dp base size
2. Define action labels in [`smarttubetv/src/main/res/values/strings.xml`](https://github.com/yuliskov/SmartTube/blob/main/smarttubetv/src/main/res/values/strings.xml) with descriptive, localization-ready keys
3. Rebuild the project—the new buttons will render immediately in the playback overlay without modifying the presenter logic

## Summary

- **Player Actions** in SmartTube connect user interface controls to playback logic through the `VideoPlayerGlue.OnActionClickedListener` interface
- The `PlayerActionListener` inner class in [`PlaybackFragment.java`](https://github.com/yuliskov/SmartTube/blob/main/PlaybackFragment.java) serves as the exclusive bridge between `VideoPlayerGlue` and `PlaybackPresenter`
- Adding custom actions requires defining a unique integer ID, instantiating a `PlaybackControlsRow.Action`, and extending the `onAction()` handler
- Long-press interactions are supported through the separate `onLongAction()` callback method
- All UI resources—including icons and labels—must be stored in the module's `res` directory following Android TV Leanback guidelines

## Frequently Asked Questions

### What is the role of VideoPlayerGlue in SmartTube?

`VideoPlayerGlue` is the controller class that manages the playback overlay UI, instantiates the `PlaybackControlsRow`, and dispatches user interactions to the `OnActionClickedListener`. It abstracts the Android Leanback library's complexity from the fragment layer, allowing `PlaybackFragment` to focus on business logic rather than view management.

### How do I handle long-press actions for custom buttons?

Implement the `onLongAction(int actionId, int actionIndex)` method within your `PlayerActionListener` implementation. This callback triggers when a user holds the select button on a remote control for approximately one second, enabling secondary functionality like "Add to Playlist" or "Show Details" without cluttering the primary interface.

### Where are action labels and icons defined in the SmartTube source?

Action labels are stored as string resources in [`smarttubetv/src/main/res/values/strings.xml`](https://github.com/yuliskov/SmartTube/blob/main/smarttubetv/src/main/res/values/strings.xml), while icons are placed as vector drawables in `smarttubetv/src/main/res/drawable/`. The `VideoPlayerGlue` constructor loads these resources when building the `PlaybackControlsRow.Action` objects, ensuring proper theming and localization support across different device configurations.

### Can I remove or modify existing default player actions?

Yes, you can modify the default control set by subclassing `VideoPlayerGlue` and overriding the method that constructs the `PlaybackControlsRow`. Remove specific `addAction()` calls for buttons you wish to hide, or replace the drawable resources in the `res/drawable` folder to change icons without altering Java code.