SmartTube Architecture Explained: Modular MVP Design for Android TV
SmartTube implements a modular MVP (Model-View-Presenter) architecture built on Android Leanback and ExoPlayer, separating concerns across distinct Gradle modules including smarttubetv, common, and exoplayer-amzn.
SmartTube is an open-source Android TV client that streams public YouTube content without requiring Google services. The yuliskov/SmartTube repository demonstrates a clean SmartTube architecture that isolates UI, business logic, and platform-specific playback concerns. This design makes it straightforward to extend individual components such as swapping ExoPlayer versions or adding new Leanback sections.
Modular Project Structure
The codebase organizes functionality across several Gradle modules, each handling a distinct concern according to the SmartTube architecture:
- smarttubetv – Core TV application containing UI components, Leanback navigation, and playback logic
- common – Shared utilities including network helpers, update URLs, and application constants
- leanbackassistant – Helper fragments and resources specific to the Leanback UI framework
- exoplayer‑amzn‑2.10.6 – Customized ExoPlayer fork optimized for Amazon Fire TV devices
- filepicker‑lib, doubletapplayerview, chatkit – Optional UI widgets for file selection, gesture controls, and live chat functionality
Application Entry Point
MainApplication serves as the global initialization point in smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/main/MainApplication.java. This class configures application-wide services before any activity launches.
// MainApplication.java
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Initialise logging, check for updates, configure Glide caching
GlideCachingModule.register(this);
Updater.checkForUpdates(this);
}
}
The application registers a Glide caching module for image loading and triggers the built-in updater to check for new releases.
Leanback Navigation Layer
The UI layer relies on Android's Leanback library, with entry points defined in launcher activities. HomeLauncherActivity extends AppCompatActivity and hosts the primary browse interface.
// HomeLauncherActivity.java
public class HomeLauncherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Attach BrowseFragment as the primary fragment
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new BrowseFragment())
.commit();
}
}
The BrowseFragment class located in smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/browse/BrowseFragment.java constructs the main content rows using a factory pattern.
// BrowseFragment.java
public class BrowseFragment extends BrowseSupportFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Create sections through the factory
BrowseSectionFragmentFactory factory = new BrowseSectionFragmentFactory(getContext());
setRows(factory.createRows());
}
}
Presenter Pattern Implementation
Presenters bridge the gap between YouTube data and Leanback adapters. VideoCardPresenter in smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/presenter/VideoCardPresenter.java handles the creation and binding of video card views.
// VideoCardPresenter.java
public class VideoCardPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
// Inflate card layout and bind click listeners
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.lb_image_card_view, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, Object item) {
VideoInfo video = (VideoInfo) item;
// Load thumbnail with Glide, attach actions
Glide.with(viewHolder.view)
.load(video.getThumbnailUrl())
.into(((ImageView) viewHolder.view.findViewById(R.id.main_image)));
}
}
This Presenter layer retrieves data via the YouTube API client, applies transformations such as SponsorBlock filtering and video-quality selection, and feeds the results to Leanback adapters.
Playback Engine and ExoPlayer Integration
PlaybackActivity owns the player lifecycle and creates ExoPlayer instances via ExoPlayerFactory. The activity hosts PlaybackFragment, which provides Leanback transport controls.
// PlaybackActivity.java
public class PlaybackActivity extends AppCompatActivity {
private SimpleExoPlayer player;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
player = ExoPlayerFactory.newSimpleInstance(this);
// Attach the fragment that shows transport controls
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new PlaybackFragment())
.commit();
}
}
Playback actions such as speed changes, HDR toggles, and SponsorBlock skips are implemented as discrete classes extending TwoStateAction. The speed control implementation in VideoSpeedAction.java demonstrates this pattern:
// VideoSpeedAction.java
public class VideoSpeedAction extends TwoStateAction {
@Override
public void onClicked() {
float newSpeed = player.getPlaybackParameters().speed == 1f ? 1.5f : 1f;
player.setPlaybackParameters(new PlaybackParameters(newSpeed));
}
}
SponsorBlock Integration
SponsorBlock functionality is accessed through SponsorBlockClient, a lightweight HTTP client that fetches segment data from the SponsorBlock API and injects skip logic into the player. The client retrieves segment boundaries for specific video IDs and schedules automatic skips when playback reaches designated start times.
// SponsorBlockClient.java (simplified)
public class SponsorBlockClient {
public List<SponsorSegment> fetchSegments(String videoId) {
// GET https://sponsor.ajay.app/api/skipSegments?videoID=...
// Parse JSON → List<SponsorSegment>
}
}
The VideoInfoAction class checks for available segments and coordinates with the player to bypass sponsored content.
Auxiliary Modules
The SmartTube architecture includes specialized modules that extend core functionality without cluttering the main codebase:
| Module | Example Class | Purpose |
|---|---|---|
| exoplayer‑amzn‑2.10.6 | ExoPlayerFactory |
Provides ExoPlayer builds tuned for Amazon Fire TV hardware |
| filepicker‑lib | FilePickerActivity |
UI for selecting local APKs or media files |
| doubletapplayerview | DoubleTapPlayView |
Handles double-tap gestures for quick forward/rewind |
| chatkit | ChatFragment |
Optional live-chat UI for streams using WebSocket connections |
Summary
- SmartTube uses a modular MVP architecture that separates UI (Leanback), business logic (Presenters), and playback (ExoPlayer) into distinct Gradle modules.
- The smarttubetv module contains the core application, while exoplayer‑amzn provides device-specific optimizations for Amazon hardware.
- Presenter classes like
VideoCardPresenteradapt YouTube API data for Leanback adapters, enabling consistent UI patterns across the application. - Playback features including SponsorBlock, variable speed, and HDR are implemented as discrete Action classes that interact with the ExoPlayer instance.
- The architecture allows developers to replace or extend individual layers—such as swapping ExoPlayer versions or adding new browse sections—without affecting the entire codebase.
Frequently Asked Questions
What architectural pattern does SmartTube use?
SmartTube implements a Model-View-Presenter (MVP) pattern combined with modular Gradle architecture. The UI layer uses Android Leanback fragments (View), the Presenter layer handles data fetching and transformation, and the Model layer consists of API clients and data repositories. This separation allows the smarttubetv module to focus purely on UI while business logic resides in presenter classes like VideoCardPresenter.
How does SmartTube handle video playback without Google services?
The application uses ExoPlayer as its playback engine, instantiated via ExoPlayerFactory within PlaybackActivity. The player streams public YouTube content through the YouTube API client rather than the official YouTube SDK, eliminating the requirement for Google Play Services. The exoplayer‑amzn module provides Amazon-specific optimizations for Fire TV devices.
How is SponsorBlock integrated into the playback flow?
SponsorBlock operates through a dedicated client class that queries the SponsorBlock API for segment data. When PlaybackActivity initializes a video, the system fetches sponsor segments and registers listeners on the ExoPlayer instance. When playback reaches a segment start time, the player automatically seeks to the end of the segment, skipping the sponsored content without user intervention.
What is the purpose of the separate exoplayer-amzn module?
The exoplayer‑amzn‑2.10.6 module maintains a customized fork of ExoPlayer specifically tuned for Amazon Fire TV hardware. This isolation allows the main smarttubetv module to remain device-agnostic while providing optimized decoder selection, DRM handling, and rendering pipelines for Amazon's specific Android implementation. Developers can update or replace this module independently without modifying the core application logic.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →