Full Code of novoda/no-player for AI

master c2e0dab24229 cached
266 files
738.5 KB
169.2k tokens
1925 symbols
1 requests
Download .txt
Showing preview only (827K chars total). Download the full file or copy to clipboard to get everything.
Repository: novoda/no-player
Branch: master
Commit: c2e0dab24229
Files: 266
Total size: 738.5 KB

Directory structure:
gitextract_28nzi1vk/

├── .github/
│   ├── contributing.md
│   ├── issue_template.md
│   ├── pull_request_template.md
│   └── workflows/
│       └── pull-request-builder.yml
├── .gitignore
├── .idea/
│   ├── codeStyleSettings.xml
│   ├── compiler.xml
│   ├── encodings.xml
│   └── vcs.xml
├── LICENSE
├── NOTICE
├── README.md
├── build.gradle
├── core/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── novoda/
│       │   │           └── noplayer/
│       │   │               ├── AndroidMediaPlayerCapabilities.java
│       │   │               ├── AspectRatioChangeCalculator.java
│       │   │               ├── ContentType.java
│       │   │               ├── DetailErrorType.java
│       │   │               ├── ExoPlayerCapabilities.java
│       │   │               ├── Listeners.java
│       │   │               ├── NoPlayer.java
│       │   │               ├── NoPlayerCreator.java
│       │   │               ├── NoPlayerError.java
│       │   │               ├── NoPlayerView.java
│       │   │               ├── Options.java
│       │   │               ├── OptionsBuilder.java
│       │   │               ├── PlayerBuilder.java
│       │   │               ├── PlayerCapabilities.java
│       │   │               ├── PlayerErrorType.java
│       │   │               ├── PlayerInformation.java
│       │   │               ├── PlayerState.java
│       │   │               ├── PlayerSurfaceHolder.java
│       │   │               ├── PlayerType.java
│       │   │               ├── PlayerView.java
│       │   │               ├── PlayerViewSurfaceHolder.java
│       │   │               ├── SubtitlePainter.java
│       │   │               ├── SubtitleView.java
│       │   │               ├── SurfaceRequester.java
│       │   │               ├── UnableToCreatePlayerException.java
│       │   │               ├── drm/
│       │   │               │   ├── DownloadedModularDrm.java
│       │   │               │   ├── DrmHandler.java
│       │   │               │   ├── DrmType.java
│       │   │               │   ├── ModularDrmKeyRequest.java
│       │   │               │   ├── ModularDrmProvisionRequest.java
│       │   │               │   └── StreamingModularDrm.java
│       │   │               ├── external/
│       │   │               │   └── exoplayer/
│       │   │               │       ├── text/
│       │   │               │       │   └── webvtt/
│       │   │               │       │       ├── CssParser.java
│       │   │               │       │       ├── WebvttCueParser.java
│       │   │               │       │       ├── WebvttDecoder.java
│       │   │               │       │       └── WebvttSubtitle.java
│       │   │               │       └── util/
│       │   │               │           └── ColorParser.java
│       │   │               ├── internal/
│       │   │               │   ├── Clock.java
│       │   │               │   ├── Heart.java
│       │   │               │   ├── SystemClock.java
│       │   │               │   ├── drm/
│       │   │               │   │   └── provision/
│       │   │               │   │       ├── HttpPostingProvisionExecutor.java
│       │   │               │   │       ├── HttpUrlConnectionPoster.java
│       │   │               │   │       ├── ProvisionExecutor.java
│       │   │               │   │       ├── ProvisionExecutorCreator.java
│       │   │               │   │       ├── ProvisioningCapabilities.java
│       │   │               │   │       └── UnableToProvisionException.java
│       │   │               │   ├── exoplayer/
│       │   │               │   │   ├── BandwidthMeterCreator.java
│       │   │               │   │   ├── CompositeTrackSelector.java
│       │   │               │   │   ├── CompositeTrackSelectorCreator.java
│       │   │               │   │   ├── ExoPlayerCreator.java
│       │   │               │   │   ├── ExoPlayerCueMapper.java
│       │   │               │   │   ├── ExoPlayerFacade.java
│       │   │               │   │   ├── ExoPlayerInformation.java
│       │   │               │   │   ├── ExoPlayerTwoImpl.java
│       │   │               │   │   ├── NoPlayerExoPlayerCreator.java
│       │   │               │   │   ├── RendererTypeRequester.java
│       │   │               │   │   ├── RendererTypeRequesterCreator.java
│       │   │               │   │   ├── SecurityDowngradingCodecSelector.java
│       │   │               │   │   ├── SimpleRenderersFactory.java
│       │   │               │   │   ├── TextRendererOutput.java
│       │   │               │   │   ├── drm/
│       │   │               │   │   │   ├── DownloadDrmSessionCreator.java
│       │   │               │   │   │   ├── DrmSessionCreator.java
│       │   │               │   │   │   ├── DrmSessionCreatorException.java
│       │   │               │   │   │   ├── DrmSessionCreatorFactory.java
│       │   │               │   │   │   ├── FrameworkDrmSession.java
│       │   │               │   │   │   ├── FrameworkMediaDrmCreator.java
│       │   │               │   │   │   ├── InvalidDrmSession.java
│       │   │               │   │   │   ├── LocalDrmSession.java
│       │   │               │   │   │   ├── LocalDrmSessionManager.java
│       │   │               │   │   │   ├── NoDrmSessionCreator.java
│       │   │               │   │   │   ├── ProvisioningModularDrmCallback.java
│       │   │               │   │   │   ├── SessionId.java
│       │   │               │   │   │   └── StreamingDrmSessionCreator.java
│       │   │               │   │   ├── error/
│       │   │               │   │   │   ├── ErrorFormatter.java
│       │   │               │   │   │   ├── ExoPlayerErrorMapper.java
│       │   │               │   │   │   ├── RendererErrorMapper.java
│       │   │               │   │   │   ├── SourceErrorMapper.java
│       │   │               │   │   │   └── UnexpectedErrorMapper.java
│       │   │               │   │   ├── forwarder/
│       │   │               │   │   │   ├── AnalyticsListenerForwarder.java
│       │   │               │   │   │   ├── BitrateForwarder.java
│       │   │               │   │   │   ├── BufferStateForwarder.java
│       │   │               │   │   │   ├── DrmSessionInfoForwarder.java
│       │   │               │   │   │   ├── EventInfoForwarder.java
│       │   │               │   │   │   ├── EventListener.java
│       │   │               │   │   │   ├── ExoPlayerDrmSessionEventListener.java
│       │   │               │   │   │   ├── ExoPlayerForwarder.java
│       │   │               │   │   │   ├── ExoPlayerVideoListener.java
│       │   │               │   │   │   ├── ForwarderInformation.java
│       │   │               │   │   │   ├── MediaSourceEventForwarder.java
│       │   │               │   │   │   ├── NoPlayerAnalyticsListener.java
│       │   │               │   │   │   ├── NoPlayerMediaSourceEventListener.java
│       │   │               │   │   │   ├── OnCompletionForwarder.java
│       │   │               │   │   │   ├── OnCompletionStateChangedForwarder.java
│       │   │               │   │   │   ├── OnPrepareForwarder.java
│       │   │               │   │   │   ├── PlayerOnErrorForwarder.java
│       │   │               │   │   │   └── VideoSizeChangedForwarder.java
│       │   │               │   │   └── mediasource/
│       │   │               │   │       ├── AudioTrackType.java
│       │   │               │   │       ├── ExoPlayerAudioTrackSelector.java
│       │   │               │   │       ├── ExoPlayerMappedTrackInfo.java
│       │   │               │   │       ├── ExoPlayerSubtitleTrackSelector.java
│       │   │               │   │       ├── ExoPlayerTrackSelector.java
│       │   │               │   │       ├── ExoPlayerVideoTrackSelector.java
│       │   │               │   │       ├── MediaSourceFactory.java
│       │   │               │   │       ├── RendererTrackIndexExtractor.java
│       │   │               │   │       └── TrackType.java
│       │   │               │   ├── listeners/
│       │   │               │   │   ├── BitrateChangedListeners.java
│       │   │               │   │   ├── BufferStateListeners.java
│       │   │               │   │   ├── CompletionListeners.java
│       │   │               │   │   ├── DroppedFramesListeners.java
│       │   │               │   │   ├── ErrorListeners.java
│       │   │               │   │   ├── HeartbeatCallbacks.java
│       │   │               │   │   ├── InfoListeners.java
│       │   │               │   │   ├── PlayerListenersHolder.java
│       │   │               │   │   ├── PreparedListeners.java
│       │   │               │   │   ├── StateChangedListeners.java
│       │   │               │   │   └── VideoSizeChangedListeners.java
│       │   │               │   ├── mediaplayer/
│       │   │               │   │   ├── AndroidMediaPlayerAudioTrackSelector.java
│       │   │               │   │   ├── AndroidMediaPlayerFacade.java
│       │   │               │   │   ├── AndroidMediaPlayerImpl.java
│       │   │               │   │   ├── AndroidMediaPlayerType.java
│       │   │               │   │   ├── BuggyVideoDriverPreventer.java
│       │   │               │   │   ├── CheckBufferHeartbeatCallback.java
│       │   │               │   │   ├── DelayedActionExecutor.java
│       │   │               │   │   ├── ErrorFactory.java
│       │   │               │   │   ├── ErrorFormatter.java
│       │   │               │   │   ├── MediaPlayerCreator.java
│       │   │               │   │   ├── MediaPlayerInformation.java
│       │   │               │   │   ├── MediaPlayerTypeReader.java
│       │   │               │   │   ├── NoPlayerMediaPlayerCreator.java
│       │   │               │   │   ├── NoPlayerTrackInfo.java
│       │   │               │   │   ├── NoPlayerTrackInfos.java
│       │   │               │   │   ├── OnPotentialBuggyDriverLayoutListener.java
│       │   │               │   │   ├── PlaybackStateChecker.java
│       │   │               │   │   ├── SystemProperties.java
│       │   │               │   │   ├── TrackInfosFactory.java
│       │   │               │   │   └── forwarder/
│       │   │               │   │       ├── BufferHeartbeatListener.java
│       │   │               │   │       ├── BufferInfoForwarder.java
│       │   │               │   │       ├── BufferOnPreparedListener.java
│       │   │               │   │       ├── CompletionForwarder.java
│       │   │               │   │       ├── CompletionInfoForwarder.java
│       │   │               │   │       ├── CompletionStateChangedForwarder.java
│       │   │               │   │       ├── ErrorForwarder.java
│       │   │               │   │       ├── ErrorInfoForwarder.java
│       │   │               │   │       ├── HeartBeatListener.java
│       │   │               │   │       ├── MediaPlayerCompletionListener.java
│       │   │               │   │       ├── MediaPlayerErrorListener.java
│       │   │               │   │       ├── MediaPlayerForwarder.java
│       │   │               │   │       ├── MediaPlayerPreparedListener.java
│       │   │               │   │       ├── OnPreparedForwarder.java
│       │   │               │   │       ├── OnPreparedInfoForwarder.java
│       │   │               │   │       ├── VideoSizeChangedForwarder.java
│       │   │               │   │       ├── VideoSizeChangedInfoForwarder.java
│       │   │               │   │       └── VideoSizeChangedListener.java
│       │   │               │   └── utils/
│       │   │               │       ├── AndroidDeviceVersion.java
│       │   │               │       ├── NoPlayerLog.java
│       │   │               │       └── Optional.java
│       │   │               ├── model/
│       │   │               │   ├── AudioTracks.java
│       │   │               │   ├── Bitrate.java
│       │   │               │   ├── Either.java
│       │   │               │   ├── KeySetId.java
│       │   │               │   ├── LoadTimeout.java
│       │   │               │   ├── NoPlayerCue.java
│       │   │               │   ├── PlayerAudioTrack.java
│       │   │               │   ├── PlayerSubtitleTrack.java
│       │   │               │   ├── PlayerVideoTrack.java
│       │   │               │   ├── TextCues.java
│       │   │               │   └── Timeout.java
│       │   │               └── text/
│       │   │                   └── NoPlayerSubtitleDecoderFactory.java
│       │   └── res/
│       │       └── layout/
│       │           └── noplayer_view.xml
│       └── test/
│           ├── java/
│           │   ├── com/
│           │   │   ├── google/
│           │   │   │   └── android/
│           │   │   │       └── exoplayer2/
│           │   │   │           ├── ExoPlaybackExceptionFactory.java
│           │   │   │           └── drm/
│           │   │   │               └── FrameworkMediaCryptoFixture.java
│           │   │   └── novoda/
│           │   │       └── noplayer/
│           │   │           ├── LoadTimeoutTest.java
│           │   │           ├── NoPlayerCreatorTest.java
│           │   │           ├── PlayerSurfaceHolderTest.java
│           │   │           ├── PlayerTypeTest.java
│           │   │           ├── internal/
│           │   │           │   ├── HeartTest.java
│           │   │           │   ├── drm/
│           │   │           │   │   └── provision/
│           │   │           │   │       ├── HttpPostingProvisionExecutorTest.java
│           │   │           │   │       └── ProvisioningCapabilitiesFixtures.java
│           │   │           │   ├── exoplayer/
│           │   │           │   │   ├── ExoPlayerFacadeTest.java
│           │   │           │   │   ├── ExoPlayerInformationTest.java
│           │   │           │   │   ├── ExoPlayerTwoImplTest.java
│           │   │           │   │   ├── NoPlayerExoPlayerCreatorTest.java
│           │   │           │   │   ├── PlayerSubtitleTrackFixture.java
│           │   │           │   │   ├── SecurityDowngradingCodecSelectorTest.java
│           │   │           │   │   ├── drm/
│           │   │           │   │   │   ├── DrmSessionCreatorFactoryTest.java
│           │   │           │   │   │   └── LocalDrmSessionManagerTest.java
│           │   │           │   │   ├── error/
│           │   │           │   │   │   └── ErrorFormatterTest.java
│           │   │           │   │   ├── forwarder/
│           │   │           │   │   │   └── ExoPlayerErrorMapperTest.java
│           │   │           │   │   └── mediasource/
│           │   │           │   │       ├── AudioFormatFixture.java
│           │   │           │   │       ├── AudioTrackTypeTest.java
│           │   │           │   │       ├── ExoPlayerAudioTrackSelectorTest.java
│           │   │           │   │       ├── ExoPlayerVideoTrackSelectorTest.java
│           │   │           │   │       ├── RendererTrackIndexExtractorTest.java
│           │   │           │   │       └── VideoFormatFixture.java
│           │   │           │   ├── listeners/
│           │   │           │   │   ├── BufferStateListenersTest.java
│           │   │           │   │   ├── CompletionListenersTest.java
│           │   │           │   │   └── StateChangedListenersTest.java
│           │   │           │   └── mediaplayer/
│           │   │           │       ├── AndroidMediaPlayerAudioTrackSelectorTest.java
│           │   │           │       ├── AndroidMediaPlayerFacadeTest.java
│           │   │           │       ├── AndroidMediaPlayerImplTest.java
│           │   │           │       ├── BuggyVideoDriverPreventerTest.java
│           │   │           │       ├── DelayedActionExecutorTest.java
│           │   │           │       ├── ErrorFactoryTest.java
│           │   │           │       ├── ErrorFormatterTest.java
│           │   │           │       ├── LoadTimeoutTest.java
│           │   │           │       ├── MediaPlayerInformationTest.java
│           │   │           │       ├── NoPlayerMediaPlayerCreatorTest.java
│           │   │           │       ├── OnPotentialBuggyDriverLayoutListenerTest.java
│           │   │           │       ├── PlaybackStateCheckerTest.java
│           │   │           │       └── PlayerCheckerTest.java
│           │   │           └── model/
│           │   │               ├── AudioTracksTest.java
│           │   │               ├── EitherTest.java
│           │   │               ├── PlayerAudioTrackFixture.java
│           │   │               └── PlayerVideoTrackFixture.java
│           │   └── utils/
│           │       └── ExceptionMatcher.java
│           └── resources/
│               └── mockito-extensions/
│                   └── org.mockito.plugins.MockMaker
├── demo/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── novoda/
│       │   │           └── demo/
│       │   │               ├── AndroidControllerView.java
│       │   │               ├── ControllerView.java
│       │   │               ├── DataPostingModularDrm.java
│       │   │               ├── DemoPresenter.java
│       │   │               ├── DialogCreator.java
│       │   │               ├── HttpClient.java
│       │   │               ├── MainActivity.java
│       │   │               ├── ProgressCalculator.java
│       │   │               └── TimeFormatter.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── progress.xml
│       │       │   └── thumb.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   ├── list_item.xml
│       │       │   └── merge_player_controls.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── colors.xml
│       │           ├── controls_styles.xml
│       │           ├── dimens.xml
│       │           ├── strings.xml
│       │           └── themes.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── novoda/
│                       └── demo/
│                           └── TimeFormatterTest.java
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── team-props/
    ├── static-analysis/
    │   ├── checkstyle-modules.xml
    │   ├── checkstyle-suppressions.xml
    │   ├── findbugs-excludes.xml
    │   ├── lint-config.xml
    │   └── pmd-rules.xml
    └── static-analysis.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/contributing.md
================================================
# Contributing to Novoda's Open Source projects

We encourage everyone inside and outside Novoda to contribute to the projects using Github's pull requests.

## Issuing a pull request

Github makes it really easy to create a pull request (PR) against a repo. Just fork it, implement your changes and create a pull request back to the original repo.

The PR should follow this format:

  * The title of the PR should be a short sentence explaining the fix
  * The PR description must contain at least two sections:
    1. **The problem**: Explain what's the bug you're trying to solve or the missing feature you're trying to add with this PR.
    2. **The solution**: Explain the fix or feature you've implemented in the PR. If this PR caused any UI change, then you should include screenshots or gifs showing how it looked before and after the change. See https://guides.github.com/features/mastering-markdown/ to create great looking markdown tables for showing your UI changes.
    3. Feel free to include funny memes or gifs ;)


## Writing tests

When you issue a PR, please take some time to consider writing tests for the issue. For example if you're solving a bug, you could write the test that reproduces the bug first, then fix the issue. This makes sure the bug doesn't come back later.

Also make sure the project builds and all the tests pass before creating the PR.

A non tested PR will not be merged back.


## Code formatting

We use a pretty standard Java code formatting:

  * Use spaces instead of tabs.
  * Indenting as explained here: http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS
  * Use curly braces for everything, even for one line `if`, `for`, etc. statements.
  * One line of white spaces between methods.
  * One space before parenthesis, curly braces, equals, etc. Such as:
    ```java
    if (value == 2) {
        value = 3;
    }
    ```

To make it easier we've made public the IDE settings we use internally so that you can import them if you want: https://github.com/novoda/novoda/tree/master/ide-settings


================================================
FILE: .github/issue_template.md
================================================
#### Problem

_Explain the problem that requires addressing_

#### Potential Solution

_Explain high level any potential solutions to the problem as you see it_

#### Impact

_Explain high level what is the impact of fixing (or not fixing) this issue:
- Are there any other components affected?
- Any future feature can benefit from this?
- Is it slowing down something?


================================================
FILE: .github/pull_request_template.md
================================================
## Problem

_Explain what is requested to do, or the problem you had to fix_

## Solution

_Explain high level how have you implemented the task, add any quirks or interesting information_

### Test(s) added 

_Explain what you did test and what you didn't, and why_

### Screenshots

| Before | After |
| ------ | ----- |
| gif/png _before_ | gif/png _after_ |

### Paired with 

_Specify @github-handle @or-handles, or write "Nobody" if you did not pair_


================================================
FILE: .github/workflows/pull-request-builder.yml
================================================
name: Pull Request Builder

on:
  pull_request:

env:
  GRADLE_USER_HOME: .gradle

jobs:

  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source-code
        uses: actions/checkout@v1
        with:
          fetch-depth: 1

      - name: Set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      - name: Synchronise cache 1/2
        uses: actions/cache@v1
        with:
          path: .gradle/wrapper
          key: gradle-cache-wrapper-${{ hashFiles('core/build.gradle') }}

      - name: Synchronise cache 2/2
        uses: actions/cache@v1
        with:
          path: .gradle/caches
          key: gradle-cache-caches-${{ hashFiles('core/build.gradle') }}

      - name: Build
        run: ./gradlew --no-daemon evaluateViolations lint test

      - name: Gather results
        if: success() || failure()
        run: |
          mkdir -p artifacts/core |
          mkdir -p artifacts/demo |
          cp -r core/build/reports/* artifacts/core |
          cp -r demo/build/reports/* artifacts/demo

      - name: Upload results
        uses: actions/upload-artifact@v1.0.0
        if: success() || failure()
        with:
          name: results
          path: artifacts


================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_

# Files for the Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# IDEA/Android Studio ignores
*.iml
*.ipr
*.iws
**/.idea/

# IDEA/Android Studio Ignore exceptions
!/.idea/copyright/
!/.idea/fileTemplates/
!/.idea/inspectionProfiles/
!/.idea/scopes/
!/.idea/codeStyleSettings.xml
!/.idea/compiler.xml
!/.idea/encodings.xml
!/.idea/vcs.xml

# OSX
*.DS_Store

# Heap dump captures
captures/


================================================
FILE: .idea/codeStyleSettings.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectCodeStyleSettingsManager">
    <option name="PER_PROJECT_SETTINGS">
      <value>
        <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
        <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
        <option name="IMPORT_LAYOUT_TABLE">
          <value>
            <package name="android" withSubpackages="true" static="false" />
            <emptyLine />
            <package name="com" withSubpackages="true" static="false" />
            <emptyLine />
            <package name="javax" withSubpackages="true" static="false" />
            <package name="java" withSubpackages="true" static="false" />
            <emptyLine />
            <package name="net" withSubpackages="true" static="false" />
            <emptyLine />
            <package name="org" withSubpackages="true" static="false" />
            <emptyLine />
            <package name="" withSubpackages="true" static="false" />
            <emptyLine />
            <package name="" withSubpackages="true" static="true" />
          </value>
        </option>
        <option name="RIGHT_MARGIN" value="150" />
        <AndroidXmlCodeStyleSettings>
          <option name="USE_CUSTOM_SETTINGS" value="true" />
          <option name="VALUE_RESOURCE_FILE_SETTINGS">
            <value>
              <option name="WRAP_ATTRIBUTES" value="4" />
            </value>
          </option>
          <option name="OTHER_SETTINGS">
            <value>
              <option name="WRAP_ATTRIBUTES" value="4" />
            </value>
          </option>
        </AndroidXmlCodeStyleSettings>
        <Objective-C-extensions>
          <file>
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
          </file>
          <class>
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
            <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
          </class>
          <extensions>
            <pair source="cpp" header="h" />
            <pair source="c" header="h" />
          </extensions>
        </Objective-C-extensions>
        <XML>
          <option name="XML_ATTRIBUTE_WRAP" value="2" />
          <option name="XML_KEEP_LINE_BREAKS" value="false" />
          <option name="XML_KEEP_BLANK_LINES" value="1" />
          <option name="XML_ALIGN_ATTRIBUTES" value="false" />
          <option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
          <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
        </XML>
        <editorconfig>
          <option name="ENABLED" value="false" />
        </editorconfig>
        <codeStyleSettings language="JAVA">
          <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
          <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
          <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
          <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
          <option name="IF_BRACE_FORCE" value="3" />
          <option name="DOWHILE_BRACE_FORCE" value="3" />
          <option name="WHILE_BRACE_FORCE" value="3" />
          <option name="FOR_BRACE_FORCE" value="3" />
        </codeStyleSettings>
        <codeStyleSettings language="XML">
          <indentOptions>
            <option name="INDENT_SIZE" value="2" />
            <option name="CONTINUATION_INDENT_SIZE" value="2" />
            <option name="TAB_SIZE" value="2" />
          </indentOptions>
          <arrangement>
            <rules>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>xmlns:android</NAME>
                      <XML_NAMESPACE>^$</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>xmlns:.*</NAME>
                      <XML_NAMESPACE>^$</XML_NAMESPACE>
                    </AND>
                  </match>
                  <order>BY_NAME</order>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:id</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:name</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>name</NAME>
                      <XML_NAMESPACE>^$</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>class</NAME>
                      <XML_NAMESPACE>^$</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>layout</NAME>
                      <XML_NAMESPACE>^$</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <NAME>style</NAME>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:theme</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:layout_width</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:layout_height</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:layout_.*</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:layout_.*</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res-auto</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/apk/res-auto</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <NAME>.*</NAME>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*:layout_.*</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/tools</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
              <section>
                <rule>
                  <match>
                    <AND>
                      <NAME>.*</NAME>
                      <XML_NAMESPACE>http://schemas.android.com/tools</XML_NAMESPACE>
                    </AND>
                  </match>
                </rule>
              </section>
            </rules>
          </arrangement>
        </codeStyleSettings>
      </value>
    </option>
    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Novoda-v2" />
  </component>
</project>

================================================
FILE: .idea/compiler.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <resourceExtensions />
    <wildcardResourcePatterns />
    <annotationProcessing>
      <profile default="true" name="Default" enabled="false">
        <outputRelativeToContentRoot value="true" />
        <processorPath useClasspath="true" />
      </profile>
    </annotationProcessing>
  </component>
</project>


================================================
FILE: .idea/encodings.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding">
    <file url="PROJECT" charset="UTF-8" />
  </component>
</project>

================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$" vcs="Git" />
  </component>
</project>

================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: NOTICE
================================================
   Copyright 2017 Novoda

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
# This project is no longer under maintenance - 13/7/2020
Upgrades to latests version of `ExoPlayer` requires a significative amount of changes. The project is no longer maintained from our end.

-----

![noplayer](art/noplayer-header.png)

[![CI status](https://github.com/novoda/no-player/workflows/Production%20Builder/badge.svg)](https://github.com/novoda/no-player/actions?query=workflow%3A%22Production+Builder%22) [![Download from Bintray](https://api.bintray.com/packages/novoda/maven/no-player/images/download.svg)](https://bintray.com/novoda/maven/no-player/_latestVersion) ![Tests](https://img.shields.io/jenkins/t/https/ci.novoda.com/view/Open%20source/job/no-player.svg) ![Coverage](https://img.shields.io/jenkins/j/https/ci.novoda.com/view/Open%20source/job/no-player.svg) [![Apache 2.0 Licence](https://img.shields.io/github/license/novoda/no-player.svg)](https://github.com/novoda/no-player/blob/master/LICENSE)

A simplified Android `Player` wrapper for [MediaPlayer](https://developer.android.com/reference/android/media/MediaPlayer.html) and [ExoPlayer](https://google.github.io/ExoPlayer/).

## Description

Some of the benefits are:

- Unified playback interface and event listeners for ExoPlayer and MediaPlayer
- `MediaPlayer` buffering
- `ExoPlayer` local, streaming and provisioning WideVine Modular DRM
- Maintains video Aspect Ratio by default
- Player selection based on `ContentType` and DRM

Experimental Features, use with caution:
- Support for TextureView

## Adding to your project

To start using this library, add these lines to the `build.gradle` of your project:

```groovy
repositories {
    jcenter()
}

dependencies {
    implementation 'com.novoda:no-player:<latest-version>'
}
```

From no-player 4.5.0 this is also needed in the android section of your `build.gradle`

```groovy
compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
}
```

### Simple usage

 1. Create a `Player`:

    ```java
    Player player = new PlayerBuilder()
      .withPriority(PlayerType.EXO_PLAYER)
      .withWidevineModularStreamingDrm(drmHandler)
      .build(this);
    ```

 2. Create the `PlayerView`:
  
    ```xml
    <com.novoda.noplayer.NoPlayerView
      android:id="@+id/player_view"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center" />
    ```

 3. Attach to a `PlayerView`:

    ```java
    PlayerView playerView = findViewById(R.id.player_view);
    player.attach(playerView);
    ```


 4. Play some content:

    ```java
    player.getListeners().addPreparedListener(playerState -> player.play());
    
    Uri uri = Uri.parse(mpdUrl);
    player.loadVideo(uri, ContentType.DASH);
    ```

## Snapshots

[![CI status](https://github.com/novoda/no-player/workflows/Snapshot%20Builder/badge.svg)](https://github.com/novoda/no-player/actions?query=workflow%3A%22Snapshot+Builder%22) [![Download from Bintray](https://api.bintray.com/packages/novoda-oss/snapshots/no-player/images/download.svg)](https://bintray.com/novoda-oss/snapshots/no-player/_latestVersion)

Snapshot builds from [`develop`](https://github.com/novoda/no-player/compare/master...develop) are automatically deployed to a [repository](https://bintray.com/novoda-oss/snapshots/no-player/_latestVersion) that is not synced with JCenter.
To consume a snapshot build add an additional maven repo as follows:
```
repositories {
    maven {
        url 'https://dl.bintray.com/novoda-oss/snapshots/'
    }
}
```

You can find the latest snapshot version following this [link](https://bintray.com/novoda-oss/snapshots/no-player/_latestVersion).

## Contributing

We always welcome people to contribute new features or bug fixes, [here is how](https://github.com/novoda/novoda/blob/master/CONTRIBUTING.md).

If you have a problem, check the [Issues Page](https://github.com/novoda/no-player/issues) first to see if we are already working on it.

Looking for community help? Browse the already asked [Stack Overflow Questions](http://stackoverflow.com/questions/tagged/support-no-player) or use the tag `support-no-player` when posting a new question.


================================================
FILE: build.gradle
================================================
allprojects {
    version = '4.5.4'
}

def teamPropsFile(propsFile) {
    def teamPropsDir = rootProject.file('team-props')
    return new File(teamPropsDir, propsFile)
}

buildscript {
    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.1'
        classpath 'com.novoda:bintray-release:0.9'
        classpath 'com.novoda:gradle-static-analysis-plugin:0.8'
        classpath 'com.novoda:gradle-build-properties-plugin:0.4.1'
        classpath "com.github.ben-manes:gradle-versions-plugin:0.20.0"
    }
}

subprojects {
    repositories {
        google()
        jcenter()
    }

    apply from: teamPropsFile('static-analysis.gradle')
}

allprojects {
    apply plugin: 'com.github.ben-manes.versions'

    dependencyUpdates.resolutionStrategy {
        componentSelection { rules ->
            rules.all { ComponentSelection selection ->
                boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm'].any { qualifier ->
                    selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
                }
                if (rejected) {
                    selection.reject('Release candidate')
                }
            }
        }
    }
}


================================================
FILE: core/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'bintray-release'
apply plugin: 'jacoco'
apply plugin: 'com.novoda.build-properties'

buildProperties {
    cli {
        using(project)
    }
    bintray {
        def bintrayCredentials = {
            boolean isDryRun = cli['dryRun'].or(true).boolean
            return isDryRun ?
                    ['bintrayRepo': 'n/a', 'bintrayUser': 'n/a', 'bintrayKey': 'n/a'] :
                    new File("${System.getenv('BINTRAY_PROPERTIES')}")
        }
        using(bintrayCredentials()).or(cli)
        description = '''This should contain the following properties:
                       | - bintrayRepo: name of the repo of the organisation to deploy the artifacts to
                       | - bintrayUser: name of the account used to deploy the artifacts
                       | - bintrayKey: API key of the account used to deploy the artifacts
        '''.stripMargin()
    }
    publish {
        def generateVersion = {
            boolean isSnapshot = cli['bintraySnapshot'].or(false).boolean
            if (isSnapshot) {
                return "SNAPSHOT-${System.getenv('BUILD_NUMBER') ?: 'LOCAL'}";
            }
            boolean isExperimental = cli['bintrayExperimental'].or(false).boolean
            if (isExperimental) {
                return "EXPERIMENTAL-${System.getenv('BUILD_NUMBER') ?: 'LOCAL'}";
            }
            return version
        }
        using(['version': "${generateVersion()}"])
                .or(buildProperties.bintray)
    }
}

android {
    compileSdkVersion 28
    buildToolsVersion '28.0.3'

    defaultConfig {
        minSdkVersion 16
        testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
    }

    lintOptions {
        lintConfig teamPropsFile('static-analysis/lint-config.xml')
        abortOnError true
        warningsAsErrors true
    }

    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'com.google.android.exoplayer:exoplayer:2.9.6'

    implementation 'com.android.support:support-annotations:28.0.0'

    testImplementation 'junit:junit:4.12'
    testImplementation 'org.mockito:mockito-core:2.27.0'
    testImplementation 'org.easytesting:fest-assert-core:2.0M10'
}

publish {
    userOrg = 'novoda'
    repoName = buildProperties.publish['bintrayRepo'].string
    groupId = 'com.novoda'
    artifactId = 'no-player'
    version = buildProperties.publish['version'].string
    bintrayUser = buildProperties.publish['bintrayUser'].string
    bintrayKey = buildProperties.publish['bintrayKey'].string
    publishVersion = version
    uploadName = 'no-player'
    desc = 'player to wrap players'
    website = 'https://github.com/novoda/no-player'
}


================================================
FILE: core/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.novoda.noplayer">

  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  <application />

</manifest>


================================================
FILE: core/src/main/java/com/novoda/noplayer/AndroidMediaPlayerCapabilities.java
================================================
package com.novoda.noplayer;

import com.novoda.noplayer.drm.DrmType;

import java.util.Arrays;
import java.util.List;

class AndroidMediaPlayerCapabilities implements PlayerCapabilities {

    private static final List<DrmType> SUPPORTED_DRM_TYPES = Arrays.asList(DrmType.NONE, DrmType.WIDEVINE_CLASSIC);

    @Override
    public boolean supports(DrmType drmType) {
        return SUPPORTED_DRM_TYPES.contains(drmType);
    }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/AspectRatioChangeCalculator.java
================================================
package com.novoda.noplayer;

class AspectRatioChangeCalculator {

    private final Listener listener;

    AspectRatioChangeCalculator(Listener listener) {
        this.listener = listener;
    }

    void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio) {
        float aspectRatio = determineAspectRatio(width, height, pixelWidthHeightRatio);
        listener.onNewAspectRatio(aspectRatio);
    }

    private float determineAspectRatio(int videoWidth, int videoHeight, float pixelWidthHeightRatio) {
        if (videoHeight == 0) {
            return 1;
        }
        return (videoWidth * pixelWidthHeightRatio) / videoHeight;
    }

    interface Listener {

        void onNewAspectRatio(float aspectRatio);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/ContentType.java
================================================
package com.novoda.noplayer;

public enum ContentType {
    H264,
    DASH,
    HLS
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/DetailErrorType.java
================================================
package com.novoda.noplayer;

/**
 * Assume all errors are thrown by Exo Player, MEDIA_PLAYER prefix will
 * indicate that the error is thrown by Media Player.
 */
public enum DetailErrorType {

    // SOURCE,
    LIVE_STALE_MANIFEST_AND_NEW_MANIFEST_COULD_NOT_LOAD_ERROR,
    PARSING_MEDIA_DATA_OR_METADATA_ERROR,

    AD_LOAD_ERROR_THEN_WILL_SKIP,
    AD_GROUP_LOAD_ERROR_THEN_WILL_SKIP,
    ALL_ADS_LOAD_ERROR_THEN_WILL_SKIP,
    ADS_LOAD_UNEXPECTED_ERROR_THEN_WILL_SKIP,

    CLIPPING_MEDIA_SOURCE_CANNOT_CLIP_WRAPPED_SOURCE_INVALID_PERIOD_COUNT,
    CLIPPING_MEDIA_SOURCE_CANNOT_CLIP_WRAPPED_SOURCE_NOT_SEEKABLE_TO_START,
    CLIPPING_MEDIA_SOURCE_CANNOT_CLIP_WRAPPED_SOURCE_START_EXCEEDS_END,
    CLIPPING_MEDIA_SOURCE_CANNOT_CLIP_WRAPPED_SOURCE_UNKNOWN_ERROR,
    DATA_POSITION_OUT_OF_RANGE_ERROR,

    SAMPLE_QUEUE_MAPPING_ERROR,
    READING_LOCAL_FILE_ERROR,
    UNEXPECTED_LOADING_ERROR,
    DOWNLOAD_ERROR,
    MERGING_MEDIA_SOURCE_CANNOT_MERGE_ITS_SOURCES,
    TASK_CANNOT_PROCEED_PRIORITY_TOO_LOW,
    CACHE_WRITING_DATA_ERROR,
    READ_LOCAL_ASSET_ERROR,

    MEDIA_PLAYER_IO,
    MEDIA_PLAYER_MALFORMED,
    MEDIA_PLAYER_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK,
    MEDIA_PLAYER_INFO_NOT_SEEKABLE,
    MEDIA_PLAYER_SUBTITLE_TIMED_OUT,
    MEDIA_PLAYER_UNSUPPORTED_SUBTITLE,

    // CONNECTIVITY
    HTTP_CANNOT_OPEN_ERROR,
    HTTP_CANNOT_READ_ERROR,
    HTTP_CANNOT_CLOSE_ERROR,
    READ_CONTENT_URI_ERROR,
    READ_FROM_UDP_ERROR,
    HLS_PLAYLIST_STUCK_SERVER_SIDE_ERROR,
    HLS_PLAYLIST_SERVER_HAS_RESET,
    MEDIA_PLAYER_TIMED_OUT,

    // DRM
    UNSUPPORTED_DRM_SCHEME_ERROR,
    DRM_INSTANTIATION_ERROR,
    DRM_UNKNOWN_ERROR,
    CANNOT_ACQUIRE_DRM_SESSION_MISSING_SCHEME_FOR_REQUIRED_UUID_ERROR,
    DRM_SESSION_ERROR,
    DRM_KEYS_EXPIRED_ERROR,
    MEDIA_REQUIRES_DRM_SESSION_MANAGER_ERROR,
    MEDIA_PLAYER_SERVER_DIED,
    MEDIA_PLAYER_PREPARE_DRM_STATUS_PREPARATION_ERROR,
    MEDIA_PLAYER_PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
    MEDIA_PLAYER_PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,

    // CONTENT_DECRYPTION
    FAIL_DECRYPT_DATA_DUE_NON_PLATFORM_COMPONENT_ERROR,
    INSUFFICIENT_OUTPUT_PROTECTION_ERROR,
    KEY_EXPIRED_ERROR,
    KEY_NOT_FOUND_WHEN_DECRYPTION_ERROR,
    RESOURCE_BUSY_ERROR_THEN_SHOULD_RETRY,
    ATTEMPTED_ON_CLOSED_SEDDION_ERROR,
    LICENSE_POLICY_REQUIRED_NOT_SUPPORTED_BY_DEVICE_ERROR,

    // RENDERER_DECODER
    AUDIO_SINK_CONFIGURATION_ERROR,
    AUDIO_SINK_INITIALISATION_ERROR,
    AUDIO_SINK_WRITE_ERROR,
    AUDIO_UNHANDLED_FORMAT_ERROR,
    AUDIO_DECODER_ERROR,
    INITIALISATION_ERROR,
    DECODING_SUBTITLE_ERROR,
    MEDIA_PLAYER_INFO_AUDIO_NOT_PLAYING,
    MEDIA_PLAYER_BAD_INTERLEAVING,
    MEDIA_PLAYER_INFO_VIDEO_NOT_PLAYING,
    MEDIA_PLAYER_INFO_VIDEO_TRACK_LAGGING,
    UNEXPECTED_CODEC_ERROR,

    // UNEXPECTED
    EGL_OPERATION_ERROR,
    SPURIOUS_AUDIO_TRACK_TIMESTAMP_ERROR,
    MULTIPLE_RENDERER_MEDIA_CLOCK_ENABLED_ERROR,

    UNKNOWN,
    MEDIA_PLAYER_UNKNOWN
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/ExoPlayerCapabilities.java
================================================
package com.novoda.noplayer;

import com.novoda.noplayer.drm.DrmType;

import java.util.Arrays;
import java.util.List;

class ExoPlayerCapabilities implements PlayerCapabilities {

    private static final List<DrmType> SUPPORTED_DRM_TYPES = Arrays.asList(
            DrmType.NONE,
            DrmType.WIDEVINE_MODULAR_STREAM,
            DrmType.WIDEVINE_MODULAR_DOWNLOAD
    );

    @Override
    public boolean supports(DrmType drmType) {
        return SUPPORTED_DRM_TYPES.contains(drmType);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/Listeners.java
================================================
package com.novoda.noplayer;

public interface Listeners {

    /**
     * Add an {@link NoPlayer.ErrorListener} to be notified of Player errors.
     *
     * @param errorListener to notify.
     */
    void addErrorListener(NoPlayer.ErrorListener errorListener);

    /**
     * Remove a given {@link NoPlayer.ErrorListener}.
     *
     * @param errorListener to remove.
     */
    void removeErrorListener(NoPlayer.ErrorListener errorListener);

    /**
     * Add a {@link NoPlayer.PreparedListener} to be notified when the {@link NoPlayer}
     * is prepared and playback can begin.
     *
     * @param preparedListener to notify.
     */
    void addPreparedListener(NoPlayer.PreparedListener preparedListener);

    /**
     * Remove a given {@link NoPlayer.PreparedListener}.
     *
     * @param preparedListener to remove.
     */
    void removePreparedListener(NoPlayer.PreparedListener preparedListener);

    /**
     * Add a {@link NoPlayer.BufferStateListener} to be notified of buffer state events.
     *
     * @param bufferStateListener to notify.
     */
    void addBufferStateListener(NoPlayer.BufferStateListener bufferStateListener);

    /**
     * Remove a given {@link NoPlayer.BufferStateListener}.
     *
     * @param bufferStateListener to remove.
     */
    void removeBufferStateListener(NoPlayer.BufferStateListener bufferStateListener);

    /**
     * Add a {@link NoPlayer.CompletionListener} to be notified when a media asset has completed playback.
     *
     * @param completionListener to notify.
     */
    void addCompletionListener(NoPlayer.CompletionListener completionListener);

    /**
     * Remove a given {@link NoPlayer.CompletionListener}.
     *
     * @param completionListener to remove.
     */
    void removeCompletionListener(NoPlayer.CompletionListener completionListener);

    /**
     * Add a {@link NoPlayer.StateChangedListener} to be notified of Player state changes.
     * e.g. Play/Pause/Stop
     *
     * @param stateChangedListener to notify.
     */
    void addStateChangedListener(NoPlayer.StateChangedListener stateChangedListener);

    /**
     * Remove a given {@link NoPlayer.StateChangedListener}.
     *
     * @param stateChangedListener to remove.
     */
    void removeStateChangedListener(NoPlayer.StateChangedListener stateChangedListener);

    /**
     * Add an {@link NoPlayer.InfoListener} to be notified of internal player callbacks
     * with additional information.
     *
     * @param infoListener to notify.
     */
    void addInfoListener(NoPlayer.InfoListener infoListener);

    /**
     * Remove a given {@link NoPlayer.InfoListener}.
     *
     * @param infoListener to remove.
     */
    void removeInfoListener(NoPlayer.InfoListener infoListener);

    /**
     * Add a {@link NoPlayer.BitrateChangedListener} to be notified of video and audio bitrate changes.
     *
     * @param bitrateChangedListener to notify.
     */
    void addBitrateChangedListener(NoPlayer.BitrateChangedListener bitrateChangedListener);

    /**
     * Remove a given {@link NoPlayer.BitrateChangedListener}.
     *
     * @param bitrateChangedListener to remove.
     */
    void removeBitrateChangedListener(NoPlayer.BitrateChangedListener bitrateChangedListener);

    /**
     * Add a {@link NoPlayer.HeartbeatCallback} to be notified on every tick of playback with a {@link NoPlayer}.
     *
     * @param heartbeatCallback to notify.
     */
    void addHeartbeatCallback(NoPlayer.HeartbeatCallback heartbeatCallback);

    /**
     * Remove a given {@link NoPlayer.HeartbeatCallback}.
     *
     * @param heartbeatCallback to remove.
     */
    void removeHeartbeatCallback(NoPlayer.HeartbeatCallback heartbeatCallback);

    /**
     * Add a {@link NoPlayer.VideoSizeChangedListener} to be notified whenever the video size changes.
     *
     * @param videoSizeChangedListener to notify.
     */
    void addVideoSizeChangedListener(NoPlayer.VideoSizeChangedListener videoSizeChangedListener);

    /**
     * Remove a given {@link NoPlayer.VideoSizeChangedListener}.
     *
     * @param videoSizeChangedListener to remove.
     */
    void removeVideoSizeChangedListener(NoPlayer.VideoSizeChangedListener videoSizeChangedListener);

    /**
     * Add a given {@link NoPlayer.DroppedVideoFramesListener} to be notified when video playback drops frames
     *
     * @param droppedVideoFramesListener to notify
     */
    void addDroppedVideoFrames(NoPlayer.DroppedVideoFramesListener droppedVideoFramesListener);

    /**
     * Remove a given {@link NoPlayer.DroppedVideoFramesListener}.
     *
     * @param droppedVideoFramesListener to remove.
     */
    void removeDroppedVideoFrames(NoPlayer.DroppedVideoFramesListener droppedVideoFramesListener);
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/NoPlayer.java
================================================
package com.novoda.noplayer;

import android.net.Uri;
import android.support.annotation.FloatRange;

import com.novoda.noplayer.internal.utils.Optional;
import com.novoda.noplayer.model.AudioTracks;
import com.novoda.noplayer.model.Bitrate;
import com.novoda.noplayer.model.PlayerAudioTrack;
import com.novoda.noplayer.model.PlayerSubtitleTrack;
import com.novoda.noplayer.model.PlayerVideoTrack;
import com.novoda.noplayer.model.Timeout;

import java.util.List;
import java.util.Map;

public interface NoPlayer extends PlayerState {

    /**
     * Retrieves a holder, which allows you to add and remove listeners on the Player.
     *
     * @return {@link Listeners} holder.
     */
    Listeners getListeners();

    /**
     * Plays content of a prepared Player.
     *
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    void play() throws IllegalStateException;

    /**
     * Deprecated: This does not perform the way it was originally intended. A seek can, and most likely will,
     * occur after the content has already started playing. This can lead to some unexpected behaviour.
     * Plays content of a prepared Player at a given position. Use {@link #loadVideo(Uri, Options)} passing
     * a initial position to the {@link Options}.
     *
     * @param positionInMillis to start playing content from.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    @Deprecated
    void playAt(long positionInMillis) throws IllegalStateException;

    /**
     * Pauses content of a prepared Player.
     *
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    void pause() throws IllegalStateException;

    /**
     * Seeks content of a prepared Player to a given position.
     * Will not cause content to play if not already playing.
     *
     * @param positionInMillis to seek content to.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    void seekTo(long positionInMillis) throws IllegalStateException;

    /**
     * Stops playback of content and then requires call to {@link NoPlayer#loadVideo(Uri, Options)} to continue playback.
     */
    void stop();

    /**
     * Stops playback of content and drops all internal resources. The instance of Player should not be
     * used after calling release.
     */
    void release();

    /**
     * Loads the video content and triggers the {@link NoPlayer.PreparedListener}.
     *
     * @param uri     link to the content.
     * @param options to be passed to the underlying player.
     * @throws IllegalStateException - if called before {@link NoPlayer#attach(PlayerView)}.
     */
    void loadVideo(Uri uri, Options options) throws IllegalStateException;

    /**
     * Loads the video content and triggers the {@link NoPlayer.PreparedListener}.
     *
     * @param uri                 link to the content.
     * @param options             to be passed to the underlying player.
     * @param timeout             amount of time to wait before triggering {@link LoadTimeoutCallback}.
     * @param loadTimeoutCallback callback when loading has hit the timeout.
     * @throws IllegalStateException - if called before {@link NoPlayer#attach(PlayerView)}.
     */
    void loadVideoWithTimeout(Uri uri, Options options, Timeout timeout, LoadTimeoutCallback loadTimeoutCallback);

    /**
     * Supplies information about the underlying player.
     *
     * @return {@link PlayerInformation}.
     */
    PlayerInformation getPlayerInformation();

    /**
     * Attaches a given {@link PlayerView} to the Player.
     *
     * @param playerView for displaying video content.
     */
    void attach(PlayerView playerView);

    /**
     * Detaches a given {@link PlayerView} from the Player.
     *
     * @param playerView for displaying video content.
     */
    void detach(PlayerView playerView);

    /**
     * Retrieves all of the available {@link PlayerVideoTrack} of a prepared Player.
     *
     * @return a list of available {@link PlayerVideoTrack} of a prepared Player.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    List<PlayerVideoTrack> getVideoTracks() throws IllegalStateException;

    /**
     * Selects a given {@link PlayerVideoTrack}.
     *
     * @param videoTrack the video track to select.
     * @return whether the selection was successful.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    boolean selectVideoTrack(PlayerVideoTrack videoTrack) throws IllegalStateException;

    /**
     * Retrieves the currently playing {@link PlayerVideoTrack} of a prepared Player wrapped
     * as an {@link Optional} or {@link Optional#absent()} if unavailable.
     *
     * @return {@link PlayerVideoTrack}.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    Optional<PlayerVideoTrack> getSelectedVideoTrack() throws IllegalStateException;

    /**
     * Clears the {@link PlayerVideoTrack} selection made in {@link NoPlayer#selectVideoTrack(PlayerVideoTrack)}.
     *
     * @return whether the clear was successful.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    boolean clearVideoTrackSelection() throws IllegalStateException;

    /**
     * Retrieves all of the available {@link PlayerAudioTrack} of a prepared Player.
     *
     * @return {@link AudioTracks} that contains a list of available {@link PlayerAudioTrack}.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    AudioTracks getAudioTracks() throws IllegalStateException;

    /**
     * Selects a given {@link PlayerAudioTrack}.
     *
     * @param audioTrack the audio track to select.
     * @return whether the selection was successful.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    boolean selectAudioTrack(PlayerAudioTrack audioTrack) throws IllegalStateException;

    /**
     * Clears the {@link PlayerAudioTrack} selection made in {@link NoPlayer#selectAudioTrack(PlayerAudioTrack)}.
     *
     * @return whether the clear was successful.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    boolean clearAudioTrackSelection() throws IllegalStateException;

    /**
     * Retrieves all of the available {@link PlayerSubtitleTrack} of a prepared Player.
     *
     * @return A list of available {@link PlayerSubtitleTrack}.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     * @see NoPlayer.PreparedListener
     */
    List<PlayerSubtitleTrack> getSubtitleTracks() throws IllegalStateException;

    /**
     * Selects and shows a given {@link PlayerSubtitleTrack} on an attached PlayerView.
     *
     * @param subtitleTrack the subtitle track to select.
     * @return whether the selection was successful.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    boolean showSubtitleTrack(PlayerSubtitleTrack subtitleTrack) throws IllegalStateException;

    /**
     * Clear and hide the subtitles on an attached PlayerView.
     *
     * @return whether the hide was successful.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    boolean hideSubtitleTrack() throws IllegalStateException;

    /**
     * Set the Player to repeat the content.
     *
     * @param repeating true to set repeating, false to disable.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    void setRepeating(boolean repeating) throws IllegalStateException;

    /**
     * Set the audio volume, with 0 being silence and 1 being unity gain.
     *
     * @param volume The audio volume.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    void setVolume(@FloatRange(from = 0.0f, to = 1.0f) float volume) throws IllegalStateException;

    /**
     * Return the audio volume, with 0 being silence and 1 being unity gain.
     *
     * @return audio volume.
     * @throws IllegalStateException - if called before {@link NoPlayer#loadVideo(Uri, Options)}.
     */
    @FloatRange(from = 0.0f, to = 1.0f)
    float getVolume() throws IllegalStateException;

    /**
     * Clears the maximum video bitrate, if set.
     */
    void clearMaxVideoBitrate();

    /**
     * Sets a maximum video bitrate. If the content is playing, the video will switch to a different quality.
     *
     * @param maxVideoBitrate The maximum video bitrate in bit per second.
     */
    void setMaxVideoBitrate(int maxVideoBitrate);

    interface PlayerError {

        PlayerErrorType type();

        DetailErrorType detailType();

        String message();
    }

    interface ErrorListener {

        void onError(PlayerError error);
    }

    interface PreparedListener {

        void onPrepared(PlayerState playerState);
    }

    interface BufferStateListener {

        void onBufferStarted();

        void onBufferCompleted();
    }

    interface CompletionListener {

        void onCompletion();
    }

    interface StateChangedListener {

        void onVideoPlaying();

        void onVideoPaused();

        void onVideoStopped();
    }

    interface BitrateChangedListener {

        void onBitrateChanged(Bitrate audioBitrate, Bitrate videoBitrate);
    }

    interface VideoSizeChangedListener {

        void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio);
    }

    /**
     * A listener for debugging information.
     */
    interface InfoListener {

        /**
         * All event listeners attached to implementations of {@link NoPlayer} will
         * forward information through this to provide debugging
         * information to client applications.
         *
         * @param callingMethod       The method name from where this call originated.
         * @param callingMethodParams Parameter name and value pairs from where this call originated.
         *                            Pass only string representations not whole objects.
         */
        void onNewInfo(String callingMethod, Map<String, String> callingMethodParams);
    }

    interface LoadTimeoutCallback {

        LoadTimeoutCallback NULL_IMPL = new LoadTimeoutCallback() {
            @Override
            public void onLoadTimeout() {
                // do nothing
            }
        };

        void onLoadTimeout();
    }

    interface HeartbeatCallback {

        void onBeat(NoPlayer player);
    }

    interface DroppedVideoFramesListener {

        void onDroppedVideoFrames(int droppedFrames, long elapsedMsSinceLastDroppedFrames);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/NoPlayerCreator.java
================================================
package com.novoda.noplayer;

import android.content.Context;

import com.novoda.noplayer.drm.DrmHandler;
import com.novoda.noplayer.drm.DrmType;
import com.novoda.noplayer.internal.exoplayer.NoPlayerExoPlayerCreator;
import com.novoda.noplayer.internal.exoplayer.drm.DrmSessionCreator;
import com.novoda.noplayer.internal.exoplayer.drm.DrmSessionCreatorException;
import com.novoda.noplayer.internal.exoplayer.drm.DrmSessionCreatorFactory;
import com.novoda.noplayer.internal.mediaplayer.NoPlayerMediaPlayerCreator;

import java.util.List;

class NoPlayerCreator {

    private final Context context;
    private final List<PlayerType> prioritizedPlayerTypes;
    private final NoPlayerExoPlayerCreator noPlayerExoPlayerCreator;
    private final NoPlayerMediaPlayerCreator noPlayerMediaPlayerCreator;
    private final DrmSessionCreatorFactory drmSessionCreatorFactory;

    NoPlayerCreator(Context context,
                    List<PlayerType> prioritizedPlayerTypes,
                    NoPlayerExoPlayerCreator noPlayerExoPlayerCreator,
                    NoPlayerMediaPlayerCreator noPlayerMediaPlayerCreator,
                    DrmSessionCreatorFactory drmSessionCreatorFactory) {
        this.context = context;
        this.prioritizedPlayerTypes = prioritizedPlayerTypes;
        this.noPlayerExoPlayerCreator = noPlayerExoPlayerCreator;
        this.noPlayerMediaPlayerCreator = noPlayerMediaPlayerCreator;
        this.drmSessionCreatorFactory = drmSessionCreatorFactory;
    }

    NoPlayer create(DrmType drmType, DrmHandler drmHandler, boolean downgradeSecureDecoder, boolean allowCrossProtocolRedirects) {
        for (PlayerType player : prioritizedPlayerTypes) {
            if (player.supports(drmType)) {
                return createPlayerForType(player, drmType, drmHandler, downgradeSecureDecoder, allowCrossProtocolRedirects);
            }
        }
        throw UnableToCreatePlayerException.unhandledDrmType(drmType);
    }

    private NoPlayer createPlayerForType(PlayerType playerType,
                                         DrmType drmType,
                                         DrmHandler drmHandler,
                                         boolean downgradeSecureDecoder,
                                         boolean allowCrossProtocolRedirects) {
        switch (playerType) {
            case MEDIA_PLAYER:
                return noPlayerMediaPlayerCreator.createMediaPlayer(context);
            case EXO_PLAYER:
                try {
                    DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(drmType, drmHandler);
                    return noPlayerExoPlayerCreator.createExoPlayer(
                            context,
                            drmSessionCreator,
                            downgradeSecureDecoder,
                            allowCrossProtocolRedirects
                    );
                } catch (DrmSessionCreatorException exception) {
                    throw new UnableToCreatePlayerException(exception);
                }
            default:
                throw UnableToCreatePlayerException.unhandledPlayerType(playerType);
        }
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/NoPlayerError.java
================================================
package com.novoda.noplayer;

public class NoPlayerError implements NoPlayer.PlayerError {

    private final PlayerErrorType playerErrorType;
    private final DetailErrorType detailErrorType;
    private final String message;

    public NoPlayerError(PlayerErrorType playerErrorType, DetailErrorType detailErrorType, String message) {
        this.playerErrorType = playerErrorType;
        this.detailErrorType = detailErrorType;
        this.message = message;
    }

    @Override
    public PlayerErrorType type() {
        return playerErrorType;
    }

    @Override
    public DetailErrorType detailType() {
        return detailErrorType;
    }

    @Override
    public String message() {
        return message;
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/NoPlayerView.java
================================================
package com.novoda.noplayer;

import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.novoda.noplayer.model.TextCues;

public class NoPlayerView extends FrameLayout implements AspectRatioChangeCalculator.Listener, PlayerView {

    private final AspectRatioChangeCalculator aspectRatioChangeCalculator;

    private AspectRatioFrameLayout videoFrame;
    private SurfaceView surfaceView;
    private SubtitleView subtitleView;
    private View shutterView;
    private PlayerSurfaceHolder playerSurfaceHolder;

    public NoPlayerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        aspectRatioChangeCalculator = new AspectRatioChangeCalculator(this);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        View.inflate(getContext(), R.layout.noplayer_view, this);
        videoFrame = findViewById(R.id.video_frame);
        shutterView = findViewById(R.id.shutter);
        surfaceView = findViewById(R.id.surface_view);
        subtitleView = findViewById(R.id.subtitles_layout);
        playerSurfaceHolder = PlayerSurfaceHolder.create(surfaceView);
    }

    @Override
    public void onNewAspectRatio(float aspectRatio) {
        videoFrame.setAspectRatio(aspectRatio);
    }

    @Override
    public View getContainerView() {
        return surfaceView;
    }

    @Override
    public PlayerSurfaceHolder getPlayerSurfaceHolder() {
        return playerSurfaceHolder;
    }

    @Override
    public NoPlayer.VideoSizeChangedListener getVideoSizeChangedListener() {
        return videoSizeChangedListener;
    }

    @Override
    public NoPlayer.StateChangedListener getStateChangedListener() {
        return stateChangedListener;
    }

    @Override
    public void showSubtitles() {
        subtitleView.setVisibility(VISIBLE);
    }

    @Override
    public void hideSubtitles() {
        subtitleView.setVisibility(GONE);
    }

    @Override
    public void setSubtitleCue(TextCues textCues) {
        subtitleView.setCues(textCues);
    }

    private final NoPlayer.VideoSizeChangedListener videoSizeChangedListener = new NoPlayer.VideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
            aspectRatioChangeCalculator.onVideoSizeChanged(width, height, pixelWidthHeightRatio);
        }
    };

    private final NoPlayer.StateChangedListener stateChangedListener = new NoPlayer.StateChangedListener() {
        @Override
        public void onVideoPlaying() {
            shutterView.setVisibility(INVISIBLE);
        }

        @Override
        public void onVideoPaused() {
            // We don't care
        }

        @Override
        public void onVideoStopped() {
            shutterView.setVisibility(VISIBLE);
        }
    };
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/Options.java
================================================
package com.novoda.noplayer;

import com.novoda.noplayer.internal.utils.Optional;

/**
 * Options to customise the underlying player.
 */
public class Options {

    private final ContentType contentType;
    private final int minDurationBeforeQualityIncreaseInMillis;
    private final int maxInitialBitrate;
    private final int maxVideoBitrate;
    private final Optional<Long> initialPositionInMillis;

    /**
     * Creates a {@link OptionsBuilder} from this Options.
     *
     * @return a new instance of {@link OptionsBuilder}.
     */
    public OptionsBuilder toOptionsBuilder() {
        OptionsBuilder optionsBuilder = new OptionsBuilder()
                .withContentType(contentType)
                .withMinDurationBeforeQualityIncreaseInMillis(minDurationBeforeQualityIncreaseInMillis)
                .withMaxInitialBitrate(maxInitialBitrate)
                .withMaxVideoBitrate(maxVideoBitrate);

        if (initialPositionInMillis.isPresent()) {
            optionsBuilder = optionsBuilder.withInitialPositionInMillis(initialPositionInMillis.get());
        }
        return optionsBuilder;
    }

    Options(ContentType contentType,
            int minDurationBeforeQualityIncreaseInMillis,
            int maxInitialBitrate,
            int maxVideoBitrate,
            Optional<Long> initialPositionInMillis) {
        this.contentType = contentType;
        this.minDurationBeforeQualityIncreaseInMillis = minDurationBeforeQualityIncreaseInMillis;
        this.maxInitialBitrate = maxInitialBitrate;
        this.maxVideoBitrate = maxVideoBitrate;
        this.initialPositionInMillis = initialPositionInMillis;
    }

    public ContentType contentType() {
        return contentType;
    }

    public int minDurationBeforeQualityIncreaseInMillis() {
        return minDurationBeforeQualityIncreaseInMillis;
    }

    public int maxInitialBitrate() {
        return maxInitialBitrate;
    }

    public int maxVideoBitrate() {
        return maxVideoBitrate;
    }

    public Optional<Long> getInitialPositionInMillis() {
        return initialPositionInMillis;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Options options = (Options) o;

        if (minDurationBeforeQualityIncreaseInMillis != options.minDurationBeforeQualityIncreaseInMillis) {
            return false;
        }
        if (maxInitialBitrate != options.maxInitialBitrate) {
            return false;
        }
        if (maxVideoBitrate != options.maxVideoBitrate) {
            return false;
        }
        if (contentType != options.contentType) {
            return false;
        }
        return initialPositionInMillis != null
                ? initialPositionInMillis.equals(options.initialPositionInMillis) : options.initialPositionInMillis == null;
    }

    @Override
    public int hashCode() {
        int result = contentType != null ? contentType.hashCode() : 0;
        result = 31 * result + minDurationBeforeQualityIncreaseInMillis;
        result = 31 * result + maxInitialBitrate;
        result = 31 * result + maxVideoBitrate;
        result = 31 * result + (initialPositionInMillis != null ? initialPositionInMillis.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Options{"
                + "contentType=" + contentType
                + ", minDurationBeforeQualityIncreaseInMillis=" + minDurationBeforeQualityIncreaseInMillis
                + ", maxInitialBitrate=" + maxInitialBitrate
                + ", maxVideoBitrate=" + maxVideoBitrate
                + ", initialPositionInMillis=" + initialPositionInMillis
                + '}';
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/OptionsBuilder.java
================================================
package com.novoda.noplayer;

import android.net.Uri;

import com.novoda.noplayer.internal.utils.Optional;

/**
 * Builds instances of {@link Options} for {@link NoPlayer#loadVideo(Uri, Options)}.
 */
public class OptionsBuilder {

    private static final int DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS = 10000;
    private static final int DEFAULT_MAX_INITIAL_BITRATE = 800000;
    private static final int DEFAULT_MAX_VIDEO_BITRATE = Integer.MAX_VALUE;

    private ContentType contentType = ContentType.H264;
    private int minDurationBeforeQualityIncreaseInMillis = DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS;
    private int maxInitialBitrate = DEFAULT_MAX_INITIAL_BITRATE;
    private int maxVideoBitrate = DEFAULT_MAX_VIDEO_BITRATE;
    private Optional<Long> initialPositionInMillis = Optional.absent();

    /**
     * Sets {@link OptionsBuilder} to build {@link Options} with a given {@link ContentType}.
     * This content type is passed to the underlying Player.
     *
     * @param contentType format of the content.
     * @return {@link OptionsBuilder}.
     */
    public OptionsBuilder withContentType(ContentType contentType) {
        this.contentType = contentType;
        return this;
    }

    /**
     * Sets {@link OptionsBuilder} to build {@link Options} so that the {@link NoPlayer}
     * switches to a higher quality video track after given time.
     *
     * @param minDurationInMillis time elapsed before switching to a higher quality video track.
     * @return {@link OptionsBuilder}.
     */
    public OptionsBuilder withMinDurationBeforeQualityIncreaseInMillis(int minDurationInMillis) {
        this.minDurationBeforeQualityIncreaseInMillis = minDurationInMillis;
        return this;
    }

    /**
     * Sets {@link OptionsBuilder} to build {@link Options} with given maximum initial bitrate in order to
     * control what is the quality with which {@link NoPlayer} starts the playback. Setting a higher value
     * allows the player to choose a higher quality video track at the beginning.
     *
     * @param maxInitialBitrate maximum bitrate that limits the initial track selection.
     * @return {@link OptionsBuilder}.
     */
    public OptionsBuilder withMaxInitialBitrate(int maxInitialBitrate) {
        this.maxInitialBitrate = maxInitialBitrate;
        return this;
    }

    /**
     * Sets {@link OptionsBuilder} to build {@link Options} with given maximum video bitrate in order to
     * control what is the maximum video quality with which {@link NoPlayer} starts the playback. Setting a higher value
     * allows the player to choose a higher quality video track.
     *
     * @param maxVideoBitrate maximum bitrate that limits the initial track selection.
     * @return {@link OptionsBuilder}
     */
    public OptionsBuilder withMaxVideoBitrate(int maxVideoBitrate) {
        this.maxVideoBitrate = maxVideoBitrate;
        return this;
    }

    /**
     * Sets {@link OptionsBuilder} to build {@link Options} with given initial position in millis in order
     * to specify the start position of the content that will be played. Omitting to set this will start
     * playback at the beginning of the content.
     *
     * @param initialPositionInMillis position that the content should begin playback at.
     * @return {@link OptionsBuilder}.
     */
    public OptionsBuilder withInitialPositionInMillis(long initialPositionInMillis) {
        this.initialPositionInMillis = Optional.of(initialPositionInMillis);
        return this;
    }

    /**
     * Builds a new {@link Options} instance.
     *
     * @return a {@link Options} instance.
     */
    public Options build() {
        return new Options(
                contentType,
                minDurationBeforeQualityIncreaseInMillis,
                maxInitialBitrate,
                maxVideoBitrate,
                initialPositionInMillis
        );
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerBuilder.java
================================================
package com.novoda.noplayer;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;

import com.novoda.noplayer.drm.DownloadedModularDrm;
import com.novoda.noplayer.drm.DrmHandler;
import com.novoda.noplayer.drm.DrmType;
import com.novoda.noplayer.drm.StreamingModularDrm;
import com.novoda.noplayer.internal.drm.provision.ProvisionExecutorCreator;
import com.novoda.noplayer.internal.exoplayer.NoPlayerExoPlayerCreator;
import com.novoda.noplayer.internal.exoplayer.drm.DrmSessionCreatorFactory;
import com.novoda.noplayer.internal.mediaplayer.NoPlayerMediaPlayerCreator;
import com.novoda.noplayer.internal.utils.AndroidDeviceVersion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Builds instances of {@link NoPlayer} for given configurations.
 */
public class PlayerBuilder {

    private DrmType drmType = DrmType.NONE;
    private DrmHandler drmHandler = DrmHandler.NO_DRM;
    private List<PlayerType> prioritizedPlayerTypes = Arrays.asList(PlayerType.EXO_PLAYER, PlayerType.MEDIA_PLAYER);
    private boolean downgradeSecureDecoder; /* initialised to false by default */
    private boolean allowCrossProtocolRedirects; /* initialised to false by default */
    private String userAgent = "user-agent";

    /**
     * Sets {@link PlayerBuilder} to build a {@link NoPlayer} which supports Widevine classic DRM.
     *
     * @return {@link PlayerBuilder}
     * @see NoPlayer
     */
    public PlayerBuilder withWidevineClassicDrm() {
        return withDrm(DrmType.WIDEVINE_CLASSIC, DrmHandler.NO_DRM);
    }

    /**
     * Sets {@link PlayerBuilder} to build a {@link NoPlayer} which supports Widevine modular streaming DRM.
     *
     * @param streamingModularDrm Implementation of {@link StreamingModularDrm}.
     * @return {@link PlayerBuilder}
     * @see NoPlayer
     */
    public PlayerBuilder withWidevineModularStreamingDrm(StreamingModularDrm streamingModularDrm) {
        return withDrm(DrmType.WIDEVINE_MODULAR_STREAM, streamingModularDrm);
    }

    /**
     * Sets {@link PlayerBuilder} to build a {@link NoPlayer} which supports Widevine modular download DRM.
     *
     * @param downloadedModularDrm Implementation of {@link DownloadedModularDrm}.
     * @return {@link PlayerBuilder}
     * @see NoPlayer
     */
    public PlayerBuilder withWidevineModularDownloadDrm(DownloadedModularDrm downloadedModularDrm) {
        return withDrm(DrmType.WIDEVINE_MODULAR_DOWNLOAD, downloadedModularDrm);
    }

    /**
     * Sets {@link PlayerBuilder} to build a {@link NoPlayer} which supports the specified parameters.
     *
     * @param drmType    {@link DrmType}
     * @param drmHandler {@link DrmHandler}
     * @return {@link PlayerBuilder}
     * @see NoPlayer
     */
    public PlayerBuilder withDrm(DrmType drmType, DrmHandler drmHandler) {
        this.drmType = drmType;
        this.drmHandler = drmHandler;
        return this;
    }

    /**
     * Sets {@link PlayerBuilder} to build a {@link NoPlayer} which will prioritise the underlying player when
     * multiple underlying players share the same features.
     *
     * @param playerType First {@link PlayerType} with the highest priority.
     * @param playerTypes Remaining {@link PlayerType} in order of priority.
     * @return {@link PlayerBuilder}
     * @see NoPlayer
     */
    public PlayerBuilder withPriority(PlayerType playerType, PlayerType... playerTypes) {
        List<PlayerType> types = new ArrayList<>();
        types.add(playerType);
        types.addAll(Arrays.asList(playerTypes));
        prioritizedPlayerTypes = types;
        return this;
    }

    /**
     * Forces secure decoder selection to be ignored in favour of using an insecure decoder.
     * e.g. Forcing an L3 stream to play with an insecure decoder instead of a secure decoder by default.
     *
     * @return {@link PlayerBuilder}
     */
    public PlayerBuilder withDowngradedSecureDecoder() {
        downgradeSecureDecoder = true;
        return this;
    }

    /**
     * @param userAgent The application's user-agent value
     * @return {@link PlayerBuilder}
     */
    public PlayerBuilder withUserAgent(String userAgent) {
        this.userAgent = userAgent;
        return this;
    }

    /**
     * Network connections will be allowed to perform redirects between HTTP and HTTPS protocols
     * @return {@link PlayerBuilder}
     */
    public PlayerBuilder allowCrossProtocolRedirects() {
        allowCrossProtocolRedirects = true;
        return this;
    }

    /**
     * Builds a new {@link NoPlayer} instance.
     *
     * @param context The {@link Context} associated with the player.
     * @return a {@link NoPlayer} instance.
     * @throws UnableToCreatePlayerException thrown when the configuration is not supported and there is no way to recover.
     * @see NoPlayer
     */
    public NoPlayer build(Context context) throws UnableToCreatePlayerException {
        Context applicationContext = context.getApplicationContext();
        Handler handler = new Handler(Looper.getMainLooper());
        ProvisionExecutorCreator provisionExecutorCreator = new ProvisionExecutorCreator();
        DrmSessionCreatorFactory drmSessionCreatorFactory = new DrmSessionCreatorFactory(
                AndroidDeviceVersion.newInstance(),
                provisionExecutorCreator,
                handler
        );
        NoPlayerCreator noPlayerCreator = new NoPlayerCreator(
                applicationContext,
                prioritizedPlayerTypes,
                NoPlayerExoPlayerCreator.newInstance(userAgent, handler),
                NoPlayerMediaPlayerCreator.newInstance(handler),
                drmSessionCreatorFactory
        );
        return noPlayerCreator.create(drmType, drmHandler, downgradeSecureDecoder, allowCrossProtocolRedirects);
    }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerCapabilities.java
================================================
package com.novoda.noplayer;

import com.novoda.noplayer.drm.DrmType;

interface PlayerCapabilities {

    boolean supports(DrmType drmType);

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerErrorType.java
================================================
package com.novoda.noplayer;

public enum PlayerErrorType {

    SOURCE,
    CONNECTIVITY,
    DRM,
    CONTENT_DECRYPTION,
    DEVICE_MEDIA_CAPABILITIES,
    RENDERER_DECODER,
    UNEXPECTED,
    UNKNOWN
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerInformation.java
================================================
package com.novoda.noplayer;

public interface PlayerInformation {

    PlayerType getPlayerType();

    String getVersion();

    String getName();
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerState.java
================================================
package com.novoda.noplayer;

public interface PlayerState {

    boolean isPlaying();

    int videoWidth();

    int videoHeight();

    long playheadPositionInMillis();

    long mediaDurationInMillis();

    int bufferPercentage();
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerSurfaceHolder.java
================================================
package com.novoda.noplayer;

import android.support.annotation.Nullable;
import android.view.SurfaceView;
import android.view.TextureView;
import com.google.android.exoplayer2.Player;

public class PlayerSurfaceHolder {

    @Nullable
    private final SurfaceView surfaceView;
    @Nullable
    private final TextureView textureView;
    private final PlayerViewSurfaceHolder surfaceHolder;

    public static PlayerSurfaceHolder create(SurfaceView surfaceView) {
        PlayerViewSurfaceHolder surfaceHolder = new PlayerViewSurfaceHolder();
        surfaceView.getHolder().addCallback(surfaceHolder);
        return new PlayerSurfaceHolder(surfaceView, null, surfaceHolder);
    }

    public static PlayerSurfaceHolder create(TextureView textureView) {
        PlayerViewSurfaceHolder surfaceHolder = new PlayerViewSurfaceHolder();
        textureView.setSurfaceTextureListener(surfaceHolder);
        return new PlayerSurfaceHolder(null, textureView, surfaceHolder);
    }

    PlayerSurfaceHolder(@Nullable SurfaceView surfaceView, @Nullable TextureView textureView, PlayerViewSurfaceHolder surfaceHolder) {
        this.surfaceView = surfaceView;
        this.textureView = textureView;
        this.surfaceHolder = surfaceHolder;
    }

    public SurfaceRequester getSurfaceRequester() {
        return surfaceHolder;
    }

    public void attach(Player.VideoComponent videoPlayer) {
        if (containsSurfaceView()) {
            videoPlayer.setVideoSurfaceView(surfaceView);
        } else if (containsTextureView()) {
            videoPlayer.setVideoTextureView(textureView);
        } else {
            throw new IllegalArgumentException("Surface container does not contain any of the expected views");
        }
    }

    private boolean containsSurfaceView() {
        return surfaceView != null;
    }

    private boolean containsTextureView() {
        return textureView != null;
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerType.java
================================================
package com.novoda.noplayer;

import com.novoda.noplayer.drm.DrmType;

public enum PlayerType {
    MEDIA_PLAYER(new AndroidMediaPlayerCapabilities()),
    EXO_PLAYER(new ExoPlayerCapabilities());

    private final PlayerCapabilities playerCapabilities;

    PlayerType(PlayerCapabilities playerCapabilities) {
        this.playerCapabilities = playerCapabilities;
    }

    boolean supports(DrmType drmType) {
        return playerCapabilities.supports(drmType);
    }

    public static PlayerType from(String rawPlayerType) {
        for (PlayerType playerType : values()) {
            if (playerType.name().equalsIgnoreCase(rawPlayerType)) {
                return playerType;
            }
        }
        throw new UnknownPlayerTypeException(rawPlayerType);
    }

    static class UnknownPlayerTypeException extends RuntimeException {

        UnknownPlayerTypeException(String rawPlayerType) {
            super("Can't create PlayerType from : " + rawPlayerType);
        }
    }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerView.java
================================================
package com.novoda.noplayer;

import android.view.View;
import com.novoda.noplayer.model.TextCues;

public interface PlayerView {

    View getContainerView();

    PlayerSurfaceHolder getPlayerSurfaceHolder();

    NoPlayer.VideoSizeChangedListener getVideoSizeChangedListener();

    NoPlayer.StateChangedListener getStateChangedListener();

    void showSubtitles();

    void hideSubtitles();

    void setSubtitleCue(TextCues textCues);

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/PlayerViewSurfaceHolder.java
================================================
package com.novoda.noplayer;

import android.graphics.SurfaceTexture;
import android.support.annotation.Nullable;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.TextureView;
import com.novoda.noplayer.model.Either;

import java.util.ArrayList;
import java.util.List;

class PlayerViewSurfaceHolder implements SurfaceHolder.Callback, TextureView.SurfaceTextureListener, SurfaceRequester {

    private final List<Callback> callbacks = new ArrayList<>();
    @Nullable
    private Either<Surface, SurfaceHolder> eitherSurface;

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        this.eitherSurface = Either.right(surfaceHolder);
        notifyListeners(eitherSurface);
        callbacks.clear();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // do nothing
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        setSurfaceNotReady();
        callbacks.clear();
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
        this.eitherSurface = Either.left(new Surface(surfaceTexture));
        notifyListeners(eitherSurface);
        callbacks.clear();
    }

    private void notifyListeners(Either<Surface, SurfaceHolder> either) {
        for (Callback callback : callbacks) {
            callback.onSurfaceReady(either);
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // do nothing
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        setSurfaceNotReady();
        surface.release();
        callbacks.clear();
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        // do nothing
    }

    private void setSurfaceNotReady() {
        eitherSurface = null;
    }

    @Override
    public void requestSurface(Callback callback) {
        if (isSurfaceReady()) {
            callback.onSurfaceReady(eitherSurface);
        } else {
            callbacks.add(callback);
        }
    }

    private boolean isSurfaceReady() {
        return eitherSurface != null;
    }

    @Override
    public void removeCallback(Callback callback) {
        callbacks.remove(callback);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/SubtitlePainter.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.novoda.noplayer;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.Layout.Alignment;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.RelativeSizeSpan;
import android.util.DisplayMetrics;
import android.util.Log;

import com.google.android.exoplayer2.text.CaptionStyleCompat;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.util.Util;
import com.novoda.noplayer.model.NoPlayerCue;

// Adopted code, could use some refactoring but it's a complex job
@SuppressWarnings({"PMD.GodClass", "PMD.CyclomaticComplexity", "PMD.StdCyclomaticComplexity", "PMD.ModifiedCyclomaticComplexity"})
final class SubtitlePainter {

    private static final String TAG = "SubtitlePainter";

    private static final float INNER_PADDING_RATIO = 0.125f;
    private static final float ROUNDING_HALF_PIXEL = 0.5f;
    private static final float TWO_DP = 2f;
    private static final double FLOAT_COMPARISON_EPSILON = .0000001;

    private final RectF lineBounds = new RectF();

    // Styled dimensions.
    private final float cornerRadius;
    private final float outlineWidth;
    private final float shadowRadius;
    private final float shadowOffset;
    private final float spacingMult;
    private final float spacingAdd;

    private final TextPaint textPaint;
    private final Paint paint;

    // Previous input variables.
    private CharSequence cueText;
    private Alignment cueTextAlignment;
    private Bitmap cueBitmap;
    private float cueLine;
    @Cue.LineType
    private int cueLineType;
    @Cue.AnchorType
    private int cueLineAnchor;
    private float cuePosition;
    @Cue.AnchorType
    private int cuePositionAnchor;
    private float cueSize;
    private float cueBitmapHeight;
    private boolean applyEmbeddedStyles;
    private boolean applyEmbeddedFontSizes;
    private int foregroundColor;
    private int backgroundColor;
    private int windowColor;
    private int edgeColor;
    @CaptionStyleCompat.EdgeType
    private int edgeType;
    private float textSizePx;
    private float bottomPaddingFraction;
    private int parentLeft;
    private int parentTop;
    private int parentRight;
    private int parentBottom;

    // Derived drawing variables.
    private StaticLayout textLayout;
    private int textLeft;
    private int textTop;
    private int textPaddingX;
    private Rect bitmapRect;

    @SuppressWarnings("ResourceType")        // We're hacking `spacingMult = styledAttributes.getFloat`
    SubtitlePainter(Context context) {
        int[] viewAttr = {android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier};
        TypedArray styledAttributes = context.obtainStyledAttributes(null, viewAttr, 0, 0);
        spacingAdd = styledAttributes.getDimensionPixelSize(0, 0);
        spacingMult = styledAttributes.getFloat(1, 1);
        styledAttributes.recycle();

        Resources resources = context.getResources();
        DisplayMetrics displayMetrics = resources.getDisplayMetrics();
        int twoDpInPx = Math.round((TWO_DP * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
        cornerRadius = twoDpInPx;
        outlineWidth = twoDpInPx;
        shadowRadius = twoDpInPx;
        shadowOffset = twoDpInPx;

        textPaint = new TextPaint();
        textPaint.setAntiAlias(true);
        textPaint.setSubpixelText(true);

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Style.FILL);
    }

    @SuppressWarnings({"checkstyle:ParameterNumber", "PMD.ExcessiveParameterList"}) // TODO group parameters into classes
    void draw(NoPlayerCue cue,
              boolean applyEmbeddedStyles,
              boolean applyEmbeddedFontSizes,
              float textSizePx,
              float bottomPaddingFraction,
              Canvas canvas,
              int cueBoxLeft,
              int cueBoxTop,
              int cueBoxRight,
              int cueBoxBottom) {
        boolean isTextCue = cue.bitmap() == null;
        int windowColor = Color.BLACK;
        if (isTextCue) {
            if (TextUtils.isEmpty(cue.text())) {
                // Nothing to draw.
                return;
            }
            windowColor = (cue.windowColorSet() && applyEmbeddedStyles)
                    ? cue.windowColor() : Color.TRANSPARENT;
        }
        if (nothingHasChanged(
                cue,
                applyEmbeddedStyles,
                applyEmbeddedFontSizes,
                textSizePx,
                bottomPaddingFraction,
                cueBoxLeft,
                cueBoxTop,
                cueBoxRight,
                cueBoxBottom,
                windowColor)) {
            // We can use the cached layout.
            drawLayout(canvas, isTextCue);
            return;
        }

        this.cueText = cue.text();
        this.cueTextAlignment = cue.textAlignment();
        this.cueBitmap = cue.bitmap();
        this.cueLine = cue.line();
        this.cueLineType = cue.lineType();
        this.cueLineAnchor = cue.lineAnchor();
        this.cuePosition = cue.position();
        this.cuePositionAnchor = cue.positionAnchor();
        this.cueSize = cue.size();
        this.cueBitmapHeight = cue.bitmapHeight();
        this.applyEmbeddedStyles = applyEmbeddedStyles;
        this.applyEmbeddedFontSizes = applyEmbeddedFontSizes;
        this.foregroundColor = Color.WHITE;
        this.backgroundColor = Color.BLACK;
        this.windowColor = windowColor;
        this.edgeType = 0;
        this.edgeColor = Color.WHITE;
        textPaint.setTypeface(null);
        this.textSizePx = textSizePx;
        this.bottomPaddingFraction = bottomPaddingFraction;
        this.parentLeft = cueBoxLeft;
        this.parentTop = cueBoxTop;
        this.parentRight = cueBoxRight;
        this.parentBottom = cueBoxBottom;

        if (isTextCue) {
            setupTextLayout();
        } else {
            setupBitmapLayout();
        }
        drawLayout(canvas, isTextCue);
    }

    @SuppressWarnings({"checkstyle:ParameterNumber", "PMD.ExcessiveParameterList"})     // TODO group parameters into classes
    private boolean nothingHasChanged(NoPlayerCue cue,
                                      boolean applyEmbeddedStyles,
                                      boolean applyEmbeddedFontSizes,
                                      float textSizePx,
                                      float bottomPaddingFraction,
                                      int cueBoxLeft,
                                      int cueBoxTop,
                                      int cueBoxRight,
                                      int cueBoxBottom,
                                      int windowColor) {
        return areCharSequencesEqual(cueText, cue.text())
                && Util.areEqual(cueTextAlignment, cue.textAlignment())
                && cueBitmap == cue.bitmap()
                && cueLine == cue.line()
                && cueLineType == cue.lineType()
                && Util.areEqual(cueLineAnchor, cue.lineAnchor())
                && cuePosition == cue.position()
                && Util.areEqual(cuePositionAnchor, cue.positionAnchor())
                && cueSize == cue.size()
                && cueBitmapHeight == cue.bitmapHeight()
                && this.applyEmbeddedStyles == applyEmbeddedStyles
                && this.applyEmbeddedFontSizes == applyEmbeddedFontSizes
                && foregroundColor == Color.WHITE
                && backgroundColor == Color.BLACK
                && this.windowColor == windowColor
                && edgeType == 0
                && edgeColor == Color.WHITE
                && Util.areEqual(textPaint.getTypeface(), null)
                && this.textSizePx == textSizePx
                && this.bottomPaddingFraction == bottomPaddingFraction
                && parentLeft == cueBoxLeft
                && parentTop == cueBoxTop
                && parentRight == cueBoxRight
                && parentBottom == cueBoxBottom;
    }

    @SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.NPathComplexity" })  // TODO break this method up
    private void setupTextLayout() {
        int parentWidth = parentRight - parentLeft;

        textPaint.setTextSize(textSizePx);
        int textPaddingX = (int) (textSizePx * INNER_PADDING_RATIO + ROUNDING_HALF_PIXEL);

        int availableWidth = parentWidth - textPaddingX * 2;
        if (isCueDimensionSet(cueSize)) {
            availableWidth *= cueSize;
        }
        if (availableWidth <= 0) {
            Log.w(TAG, "Skipped drawing subtitle cue (insufficient space)");
            return;
        }

        // Remove embedded styling or font size if requested.
        CharSequence cueText;
        if (applyEmbeddedFontSizes && applyEmbeddedStyles) {
            cueText = this.cueText;
        } else if (!applyEmbeddedStyles) {
            cueText = this.cueText.toString(); // Equivalent to erasing all spans.
        } else {
            SpannableStringBuilder newCueText = new SpannableStringBuilder(this.cueText);
            int cueLength = newCueText.length();
            AbsoluteSizeSpan[] absSpans = newCueText.getSpans(0, cueLength, AbsoluteSizeSpan.class);
            RelativeSizeSpan[] relSpans = newCueText.getSpans(0, cueLength, RelativeSizeSpan.class);
            for (AbsoluteSizeSpan absSpan : absSpans) {
                newCueText.removeSpan(absSpan);
            }
            for (RelativeSizeSpan relSpan : relSpans) {
                newCueText.removeSpan(relSpan);
            }
            cueText = newCueText;
        }

        Alignment textAlignment = cueTextAlignment == null ? Alignment.ALIGN_CENTER : cueTextAlignment;
        textLayout = new StaticLayout(cueText, textPaint, availableWidth, textAlignment, spacingMult,
                spacingAdd, true);
        int textWidth = 0;
        int lineCount = textLayout.getLineCount();
        for (int i = 0; i < lineCount; i++) {
            textWidth = Math.max((int) Math.ceil(textLayout.getLineWidth(i)), textWidth);
        }
        if (isCueDimensionSet(cueSize) && textWidth < availableWidth) {
            textWidth = availableWidth;
        }
        textWidth += textPaddingX * 2;

        int textLeft;
        int textRight;
        if (isCueDimensionSet(cuePosition)) {
            int anchorPosition = Math.round(parentWidth * cuePosition) + parentLeft;
            textLeft = cuePositionAnchor == Cue.ANCHOR_TYPE_END ? anchorPosition - textWidth
                    : cuePositionAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorPosition * 2 - textWidth) / 2
                    : anchorPosition;
            textLeft = Math.max(textLeft, parentLeft);
            textRight = Math.min(textLeft + textWidth, parentRight);
        } else {
            textLeft = (parentWidth - textWidth) / 2;
            textRight = textLeft + textWidth;
        }

        textWidth = textRight - textLeft;
        if (textWidth <= 0) {
            Log.w(TAG, "Skipped drawing subtitle cue (invalid horizontal positioning)");
            return;
        }

        int parentHeight = parentBottom - parentTop;
        int textHeight = textLayout.getHeight();

        int textTop;
        if (isCueDimensionSet(cueLine)) {
            int anchorPosition;
            if (cueLineType == Cue.LINE_TYPE_FRACTION) {
                anchorPosition = Math.round(parentHeight * cueLine) + parentTop;
            } else {
                // cueLineType == Cue.LINE_TYPE_NUMBER
                int firstLineHeight = textLayout.getLineBottom(0) - textLayout.getLineTop(0);
                if (cueLine >= 0) {
                    anchorPosition = Math.round(cueLine * firstLineHeight) + parentTop;
                } else {
                    anchorPosition = Math.round((cueLine + 1) * firstLineHeight) + parentBottom;
                }
            }
            textTop = cueLineAnchor == Cue.ANCHOR_TYPE_END ? anchorPosition - textHeight
                    : cueLineAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorPosition * 2 - textHeight) / 2
                    : anchorPosition;
            if (textTop + textHeight > parentBottom) {
                textTop = parentBottom - textHeight;
            } else if (textTop < parentTop) {
                textTop = parentTop;
            }
        } else {
            textTop = parentBottom - textHeight - (int) (parentHeight * bottomPaddingFraction);
        }

        // Update the derived drawing variables.
        this.textLayout = new StaticLayout(cueText, textPaint, textWidth, textAlignment, spacingMult,
                spacingAdd, true);
        this.textLeft = textLeft;
        this.textTop = textTop;
        this.textPaddingX = textPaddingX;
    }

    @SuppressWarnings("PMD.NPathComplexity")  // TODO break this method up
    private void setupBitmapLayout() {
        int parentWidth = parentRight - parentLeft;
        int parentHeight = parentBottom - parentTop;
        float anchorX = parentLeft + (parentWidth * cuePosition);
        float anchorY = parentTop + (parentHeight * cueLine);
        int width = Math.round(parentWidth * cueSize);

        int height = isCueDimensionSet(cueBitmapHeight)
                ? Math.round(parentHeight * cueBitmapHeight)
                : Math.round(width * ((float) cueBitmap.getHeight() / cueBitmap.getWidth()));

        int x = Math.round(cueLineAnchor == Cue.ANCHOR_TYPE_END
                ? (anchorX - width)
                : cueLineAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorX - (width / 2f)) : anchorX);

        int y = Math.round(cuePositionAnchor == Cue.ANCHOR_TYPE_END
                ? (anchorY - height)
                : cuePositionAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorY - (height / 2f)) : anchorY);

        bitmapRect = new Rect(x, y, x + width, y + height);
    }

    private boolean isCueDimensionSet(float cueDimension) {
        return Math.abs(cueDimension - Cue.DIMEN_UNSET) > FLOAT_COMPARISON_EPSILON;
    }

    private void drawLayout(Canvas canvas, boolean isTextCue) {
        if (isTextCue) {
            drawTextLayout(canvas);
        } else {
            drawBitmapLayout(canvas);
        }
    }

    @SuppressWarnings("PMD.NPathComplexity")  // TODO break this method up
    private void drawTextLayout(Canvas canvas) {
        StaticLayout layout = textLayout;
        if (layout == null) {
            // Nothing to draw.
            return;
        }

        int saveCount = canvas.save();
        canvas.translate(textLeft, textTop);

        if (Color.alpha(windowColor) > 0) {
            paint.setColor(windowColor);
            canvas.drawRect(-textPaddingX, 0, layout.getWidth() + textPaddingX, layout.getHeight(),
                    paint);
        }

        if (Color.alpha(backgroundColor) > 0) {
            paint.setColor(backgroundColor);
            float previousBottom = layout.getLineTop(0);
            int lineCount = layout.getLineCount();
            for (int i = 0; i < lineCount; i++) {
                lineBounds.left = layout.getLineLeft(i) - textPaddingX;
                lineBounds.right = layout.getLineRight(i) + textPaddingX;
                lineBounds.top = previousBottom;
                lineBounds.bottom = layout.getLineBottom(i);
                previousBottom = lineBounds.bottom;
                canvas.drawRoundRect(lineBounds, cornerRadius, cornerRadius, paint);
            }
        }

        if (edgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) {
            textPaint.setStrokeJoin(Join.ROUND);
            textPaint.setStrokeWidth(outlineWidth);
            textPaint.setColor(edgeColor);
            textPaint.setStyle(Style.FILL_AND_STROKE);
            layout.draw(canvas);
        } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) {
            textPaint.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, edgeColor);
        } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED
                || edgeType == CaptionStyleCompat.EDGE_TYPE_DEPRESSED) {
            boolean raised = edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED;
            int colorUp = raised ? Color.WHITE : edgeColor;
            int colorDown = raised ? edgeColor : Color.WHITE;
            float offset = shadowRadius / 2;
            textPaint.setColor(foregroundColor);
            textPaint.setStyle(Style.FILL);
            textPaint.setShadowLayer(shadowRadius, -offset, -offset, colorUp);
            layout.draw(canvas);
            textPaint.setShadowLayer(shadowRadius, offset, offset, colorDown);
        }

        textPaint.setColor(foregroundColor);
        textPaint.setStyle(Style.FILL);
        layout.draw(canvas);
        textPaint.setShadowLayer(0, 0, 0, 0);

        canvas.restoreToCount(saveCount);
    }

    private void drawBitmapLayout(Canvas canvas) {
        canvas.drawBitmap(cueBitmap, null, bitmapRect, null);
    }

    /**
     * This method is used instead of {@link TextUtils#equals(CharSequence, CharSequence)} because the
     * latter only checks the text of each sequence, and does not check for equality of styling that
     * may be embedded within the {@link CharSequence}s.
     */
    @SuppressWarnings("PMD.CompareObjectsWithEquals")   // We do, but we first try to shortcut by comparing references
    private static boolean areCharSequencesEqual(CharSequence first, CharSequence second) {
        // Some CharSequence implementations don't perform a cheap referential equality check in their
        // equals methods, so we perform one explicitly here.
        return first == second || (first != null && first.equals(second));
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/SubtitleView.java
================================================
package com.novoda.noplayer;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

import com.novoda.noplayer.model.TextCues;

import java.util.ArrayList;
import java.util.List;

public final class SubtitleView extends View {

    private static final float DEFAULT_TEXT_SIZE_FRACTION = 0.0533f;
    private static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f;

    private static final boolean APPLY_EMBEDDED_STYLES = true;
    private static final boolean APPLY_EMBEDDED_FONT_STYLES = true;

    private static final int ZERO_PIXELS = 0;

    private final List<SubtitlePainter> painters;

    private TextCues textCues;

    public SubtitleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        painters = new ArrayList<>();
    }

    public void setCues(TextCues textCues) {
        if (textCues.equals(this.textCues)) {
            return;
        }

        this.textCues = textCues;
        int cueCount = textCues.size();

        while (painters.size() < cueCount) {
            painters.add(new SubtitlePainter(getContext()));
        }

        invalidate();
    }

    @Override
    public void dispatchDraw(Canvas canvas) {
        if (textCues == null || textCues.isEmpty()) {
            return;
        }

        int rawTop = getTop();
        int rawBottom = getBottom();

        int left = getLeft() + getPaddingLeft();
        int top = rawTop + getPaddingTop();
        int right = getRight() + getPaddingRight();
        int bottom = rawBottom - getPaddingBottom();

        if (bottom <= top || right <= left) {
            return;
        }

        float textSizeInPixels = DEFAULT_TEXT_SIZE_FRACTION * (bottom - top);

        if (textSizeInPixels <= ZERO_PIXELS) {
            return;
        }

        int cueCount = textCues.size();
        for (int i = 0; i < cueCount; i++) {
            painters.get(i).draw(
                    textCues.get(i),
                    APPLY_EMBEDDED_STYLES,
                    APPLY_EMBEDDED_FONT_STYLES,
                    textSizeInPixels,
                    DEFAULT_BOTTOM_PADDING_FRACTION,
                    canvas,
                    left,
                    top,
                    right,
                    bottom
            );
        }
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/SurfaceRequester.java
================================================
package com.novoda.noplayer;

import android.view.Surface;
import android.view.SurfaceHolder;
import com.novoda.noplayer.model.Either;

public interface SurfaceRequester {

    void requestSurface(Callback callback);

    void removeCallback(Callback callback);

    interface Callback {

        void onSurfaceReady(Either<Surface, SurfaceHolder> surface);
    }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/UnableToCreatePlayerException.java
================================================
package com.novoda.noplayer;

import com.novoda.noplayer.drm.DrmType;
import com.novoda.noplayer.internal.utils.AndroidDeviceVersion;

public class UnableToCreatePlayerException extends RuntimeException {

    static UnableToCreatePlayerException unhandledDrmType(DrmType drmType) {
        return new UnableToCreatePlayerException("Unhandled DrmType: " + drmType);
    }

    static UnableToCreatePlayerException unhandledPlayerType(PlayerType playerType) {
        return new UnableToCreatePlayerException("Unhandled player type: " + playerType.name());
    }

    public static UnableToCreatePlayerException deviceDoesNotMeetTargetApiException(DrmType drmType,
                                                                                    int targetApiLevel,
                                                                                    AndroidDeviceVersion actualApiLevel) {
        return new UnableToCreatePlayerException(
                "Device must be target: "
                        + targetApiLevel
                        + " but was: "
                        + actualApiLevel.sdkInt()
                        + " for DRM type: "
                        + drmType.name()
        );
    }

    UnableToCreatePlayerException(Throwable cause) {
        super(cause);
    }

    private UnableToCreatePlayerException(String reason) {
        super(reason);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/drm/DownloadedModularDrm.java
================================================
package com.novoda.noplayer.drm;

import com.novoda.noplayer.model.KeySetId;

public interface DownloadedModularDrm extends DrmHandler {

    KeySetId getKeySetId();
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/drm/DrmHandler.java
================================================
package com.novoda.noplayer.drm;

@SuppressWarnings({
        "checkstyle:interfaceistype",
        "PMD.AvoidConstantsInterface",
        "PMD.ConstantsInInterface"
}) // This is to allow for multiple different types of DRM
public interface DrmHandler {

    DrmHandler NO_DRM = new DrmHandler() {
    };
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/drm/DrmType.java
================================================
package com.novoda.noplayer.drm;

public enum DrmType {
    NONE,
    WIDEVINE_CLASSIC,
    WIDEVINE_MODULAR_STREAM,
    WIDEVINE_MODULAR_DOWNLOAD
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/drm/ModularDrmKeyRequest.java
================================================
package com.novoda.noplayer.drm;

import java.util.Arrays;

public class ModularDrmKeyRequest {

    private final String url;
    private final byte[] data;

    public ModularDrmKeyRequest(String url, byte[] data) {
        this.url = url;
        this.data = Arrays.copyOf(data, data.length);
    }

    public String url() {
        return url;
    }

    public byte[] data() {
        return Arrays.copyOf(data, data.length);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        ModularDrmKeyRequest that = (ModularDrmKeyRequest) o;

        if (url != null ? !url.equals(that.url) : that.url != null) {
            return false;
        }
        return Arrays.equals(data, that.data);
    }

    @Override
    public int hashCode() {
        int result = url != null ? url.hashCode() : 0;
        result = 31 * result + Arrays.hashCode(data);
        return result;
    }

    @Override
    public String toString() {
        return "ModularDrmKeyRequest{"
                + "url='" + url + '\''
                + ", data=" + Arrays.toString(data)
                + '}';
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/drm/ModularDrmProvisionRequest.java
================================================
package com.novoda.noplayer.drm;

import java.util.Arrays;

public class ModularDrmProvisionRequest {

    private final String url;
    private final byte[] data;

    public ModularDrmProvisionRequest(String url, byte[] data) {
        this.url = url;
        this.data = Arrays.copyOf(data, data.length);
    }

    public String url() {
        return url;
    }

    public byte[] data() {
        return Arrays.copyOf(data, data.length);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        ModularDrmProvisionRequest that = (ModularDrmProvisionRequest) o;

        if (url != null ? !url.equals(that.url) : that.url != null) {
            return false;
        }
        return Arrays.equals(data, that.data);
    }

    @Override
    public int hashCode() {
        int result = url != null ? url.hashCode() : 0;
        result = 31 * result + Arrays.hashCode(data);
        return result;
    }

    @Override
    public String toString() {
        return "ModularDrmProvisionRequest{"
                + "url='" + url + '\''
                + ", data=" + Arrays.toString(data)
                + '}';
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/drm/StreamingModularDrm.java
================================================
package com.novoda.noplayer.drm;

public interface StreamingModularDrm extends DrmHandler {

    byte[] executeKeyRequest(ModularDrmKeyRequest request) throws DrmRequestException;

    final class DrmRequestException extends Exception {

        public static DrmRequestException from(Exception e) {
            return new DrmRequestException("Drm http request failed : " + e.getMessage(), e);
        }

        public static DrmRequestException invalidHttpCode(int code, String body) {
            return new DrmRequestException("Unexpected response HTTP code: " + code + " | " + body);
        }

        private DrmRequestException(String detailMessage) {
            super(detailMessage);
        }

        private DrmRequestException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/CssParser.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.novoda.noplayer.external.exoplayer.text.webvtt;

import android.text.TextUtils;

import com.google.android.exoplayer2.text.webvtt.WebvttCssStyle;
import com.google.android.exoplayer2.util.ColorParser;
import com.google.android.exoplayer2.util.ParsableByteArray;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Provides a CSS parser for STYLE blocks in Webvtt files. Supports only a subset of the CSS
 * features.
 */
/* package */ final class CssParser {

  private static final String PROPERTY_BGCOLOR = "background-color";
  private static final String PROPERTY_FONT_FAMILY = "font-family";
  private static final String PROPERTY_FONT_WEIGHT = "font-weight";
  private static final String PROPERTY_TEXT_DECORATION = "text-decoration";
  private static final String VALUE_BOLD = "bold";
  private static final String VALUE_UNDERLINE = "underline";
  private static final String BLOCK_START = "{";
  private static final String BLOCK_END = "}";
  private static final String PROPERTY_FONT_STYLE = "font-style";
  private static final String VALUE_ITALIC = "italic";

  private static final Pattern VOICE_NAME_PATTERN = Pattern.compile("\\[voice=\"([^\"]*)\"\\]");

  // Temporary utility data structures.
  private final ParsableByteArray styleInput;
  private final StringBuilder stringBuilder;

  public CssParser() {
    styleInput = new ParsableByteArray();
    stringBuilder = new StringBuilder();
  }

  /**
   * Takes a CSS style block and consumes up to the first empty line found. Attempts to parse the
   * contents of the style block and returns a {@link WebvttCssStyle} instance if successful, or
   * {@code null} otherwise.
   *
   * @param input The input from which the style block should be read.
   * @return A {@link WebvttCssStyle} that represents the parsed block.
   */
  public WebvttCssStyle parseBlock(ParsableByteArray input) {
    stringBuilder.setLength(0);
    int initialInputPosition = input.getPosition();
    skipStyleBlock(input);
    styleInput.reset(input.data, input.getPosition());
    styleInput.setPosition(initialInputPosition);
    String selector = parseSelector(styleInput, stringBuilder);
    if (selector == null || !BLOCK_START.equals(parseNextToken(styleInput, stringBuilder))) {
      return null;
    }
    WebvttCssStyle style = new WebvttCssStyle();
    applySelectorToStyle(style, selector);
    String token = null;
    boolean blockEndFound = false;
    while (!blockEndFound) {
      int position = styleInput.getPosition();
      token = parseNextToken(styleInput, stringBuilder);
      blockEndFound = token == null || BLOCK_END.equals(token);
      if (!blockEndFound) {
        styleInput.setPosition(position);
        parseStyleDeclaration(styleInput, style, stringBuilder);
      }
    }
    return BLOCK_END.equals(token) ? style : null; // Check that the style block ended correctly.
  }

  /**
   * Returns a string containing the selector. The input is expected to have the form
   * {@code ::cue(tag#id.class1.class2[voice="someone"]}, where every element is optional.
   *
   * @param input From which the selector is obtained.
   * @return A string containing the target, empty string if the selector is universal
   *     (targets all cues) or null if an error was encountered.
   */
  private static String parseSelector(ParsableByteArray input, StringBuilder stringBuilder) {
    skipWhitespaceAndComments(input);
    if (input.bytesLeft() < 5) {
      return null;
    }
    String cueSelector = input.readString(5);
    if (!"::cue".equals(cueSelector)) {
      return null;
    }
    int position = input.getPosition();
    String token = parseNextToken(input, stringBuilder);
    if (token == null) {
      return null;
    }
    if (BLOCK_START.equals(token)) {
      input.setPosition(position);
      return "";
    }
    String target = null;
    if ("(".equals(token)) {
      target = readCueTarget(input);
    }
    token = parseNextToken(input, stringBuilder);
    if (!")".equals(token) || token == null) {
      return null;
    }
    return target;
  }

  /**
   * Reads the contents of ::cue() and returns it as a string.
   */
  private static String readCueTarget(ParsableByteArray input) {
    int position = input.getPosition();
    int limit = input.limit();
    boolean cueTargetEndFound = false;
    while (position < limit && !cueTargetEndFound) {
      char c = (char) input.data[position++];
      cueTargetEndFound = c == ')';
    }
    return input.readString(--position - input.getPosition()).trim();
    // --offset to return ')' to the input.
  }

  private static void parseStyleDeclaration(ParsableByteArray input, WebvttCssStyle style,
                                            StringBuilder stringBuilder) {
    skipWhitespaceAndComments(input);
    String property = parseIdentifier(input, stringBuilder);
    if ("".equals(property)) {
      return;
    }
    if (!":".equals(parseNextToken(input, stringBuilder))) {
      return;
    }
    skipWhitespaceAndComments(input);
    String value = parsePropertyValue(input, stringBuilder);
    if (value == null || "".equals(value)) {
      return;
    }
    int position = input.getPosition();
    String token = parseNextToken(input, stringBuilder);
    if (";".equals(token)) {
      // The style declaration is well formed.
    } else if (BLOCK_END.equals(token)) {
      // The style declaration is well formed and we can go on, but the closing bracket had to be
      // fed back.
      input.setPosition(position);
    } else {
      // The style declaration is not well formed.
      return;
    }
    // At this point we have a presumably valid declaration, we need to parse it and fill the style.
    if ("color".equals(property)) {
      style.setFontColor(ColorParser.parseCssColor(value));
    } else if (PROPERTY_BGCOLOR.equals(property)) {
      style.setBackgroundColor(ColorParser.parseCssColor(value));
    } else if (PROPERTY_TEXT_DECORATION.equals(property)) {
      if (VALUE_UNDERLINE.equals(value)) {
        style.setUnderline(true);
      }
    } else if (PROPERTY_FONT_FAMILY.equals(property)) {
      style.setFontFamily(value);
    } else if (PROPERTY_FONT_WEIGHT.equals(property)) {
      if (VALUE_BOLD.equals(value)) {
        style.setBold(true);
      }
    } else if (PROPERTY_FONT_STYLE.equals(property)) {
      if (VALUE_ITALIC.equals(value)) {
        style.setItalic(true);
      }
    }
    // TODO: Fill remaining supported styles.
  }

  // Visible for testing.
  /* package */ static void skipWhitespaceAndComments(ParsableByteArray input) {
    boolean skipping = true;
    while (input.bytesLeft() > 0 && skipping) {
      skipping = maybeSkipWhitespace(input) || maybeSkipComment(input);
    }
  }

  // Visible for testing.
  /* package */ static String parseNextToken(ParsableByteArray input, StringBuilder stringBuilder) {
    skipWhitespaceAndComments(input);
    if (input.bytesLeft() == 0) {
      return null;
    }
    String identifier = parseIdentifier(input, stringBuilder);
    if (!"".equals(identifier)) {
      return identifier;
    }
    // We found a delimiter.
    return "" + (char) input.readUnsignedByte();
  }

  private static boolean maybeSkipWhitespace(ParsableByteArray input) {
    switch(peekCharAtPosition(input, input.getPosition())) {
      case '\t':
      case '\r':
      case '\n':
      case '\f':
      case ' ':
        input.skipBytes(1);
        return true;
      default:
        return false;
    }
  }

  // Visible for testing.
  /* package */ static void skipStyleBlock(ParsableByteArray input) {
    // The style block cannot contain empty lines, so we assume the input ends when a empty line
    // is found.
    String line;
    do {
      line = input.readLine();
    } while (!TextUtils.isEmpty(line));
  }

  private static char peekCharAtPosition(ParsableByteArray input, int position) {
    return (char) input.data[position];
  }

  private static String parsePropertyValue(ParsableByteArray input, StringBuilder stringBuilder) {
    StringBuilder expressionBuilder = new StringBuilder();
    String token;
    int position;
    boolean expressionEndFound = false;
    // TODO: Add support for "Strings in quotes with spaces".
    while (!expressionEndFound) {
      position = input.getPosition();
      token = parseNextToken(input, stringBuilder);
      if (token == null) {
        // Syntax error.
        return null;
      }
      if (BLOCK_END.equals(token) || ";".equals(token)) {
        input.setPosition(position);
        expressionEndFound = true;
      } else {
        expressionBuilder.append(token);
      }
    }
    return expressionBuilder.toString();
  }

  private static boolean maybeSkipComment(ParsableByteArray input) {
    int position = input.getPosition();
    int limit = input.limit();
    byte[] data = input.data;
    if (position + 2 <= limit && data[position++] == '/' && data[position++] == '*') {
      while (position + 1 < limit) {
        char skippedChar = (char) data[position++];
        if (skippedChar == '*') {
          if (((char) data[position]) == '/') {
            position++;
            limit = position;
          }
        }
      }
      input.skipBytes(limit - input.getPosition());
      return true;
    }
    return false;
  }

  private static String parseIdentifier(ParsableByteArray input, StringBuilder stringBuilder) {
    stringBuilder.setLength(0);
    int position = input.getPosition();
    int limit = input.limit();
    boolean identifierEndFound = false;
    while (position  < limit && !identifierEndFound) {
      char c = (char) input.data[position];
      if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '#'
          || c == '-' || c == '.' || c == '_') {
        position++;
        stringBuilder.append(c);
      } else {
        identifierEndFound = true;
      }
    }
    input.skipBytes(position - input.getPosition());
    return stringBuilder.toString();
  }

  /**
   * Sets the target of a {@link WebvttCssStyle} by splitting a selector of the form
   * {@code ::cue(tag#id.class1.class2[voice="someone"]}, where every element is optional.
   */
  private void applySelectorToStyle(WebvttCssStyle style, String selector) {
    if ("".equals(selector)) {
      return; // Universal selector.
    }
    int voiceStartIndex = selector.indexOf('[');
    if (voiceStartIndex != -1) {
      Matcher matcher = VOICE_NAME_PATTERN.matcher(selector.substring(voiceStartIndex));
      if (matcher.matches()) {
        style.setTargetVoice(matcher.group(1));
      }
      selector = selector.substring(0, voiceStartIndex);
    }
    String[] classDivision = selector.split("\\.");
    String tagAndIdDivision = classDivision[0];
    int idPrefixIndex = tagAndIdDivision.indexOf('#');
    if (idPrefixIndex != -1) {
      style.setTargetTagName(tagAndIdDivision.substring(0, idPrefixIndex));
      style.setTargetId(tagAndIdDivision.substring(idPrefixIndex + 1)); // We discard the '#'.
    } else {
      style.setTargetTagName(tagAndIdDivision);
    }
    if (classDivision.length > 1) {
      style.setTargetClasses(Arrays.copyOfRange(classDivision, 1, classDivision.length));
    }
  }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttCueParser.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.novoda.noplayer.external.exoplayer.text.webvtt;

import android.graphics.Typeface;
import android.support.annotation.NonNull;
import android.text.Layout.Alignment;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan;
import android.util.Log;

import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.webvtt.WebvttCssStyle;
import com.google.android.exoplayer2.text.webvtt.WebvttCue;
import com.google.android.exoplayer2.text.webvtt.WebvttParserUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.novoda.noplayer.external.exoplayer.util.ColorParser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Parser for WebVTT cues. (https://w3c.github.io/webvtt/#cues)
 */
public final class WebvttCueParser {

    public static final Pattern CUE_HEADER_PATTERN = Pattern
        .compile("^(\\S+)\\s+-->\\s+(\\S+)(.*)?$");

    private static final Pattern CUE_SETTING_PATTERN = Pattern.compile("(\\S+?):(\\S+)");

    private static final char CHAR_LESS_THAN = '<';
    private static final char CHAR_GREATER_THAN = '>';
    private static final char CHAR_SLASH = '/';
    private static final char CHAR_AMPERSAND = '&';
    private static final char CHAR_SEMI_COLON = ';';
    private static final char CHAR_SPACE = ' ';

    private static final String ENTITY_LESS_THAN = "lt";
    private static final String ENTITY_GREATER_THAN = "gt";
    private static final String ENTITY_AMPERSAND = "amp";
    private static final String ENTITY_NON_BREAK_SPACE = "nbsp";

    private static final String TAG_BOLD = "b";
    private static final String TAG_ITALIC = "i";
    private static final String TAG_UNDERLINE = "u";
    private static final String TAG_CLASS = "c";
    private static final String TAG_VOICE = "v";
    private static final String TAG_LANG = "lang";

    private static final int STYLE_BOLD = Typeface.BOLD;
    private static final int STYLE_ITALIC = Typeface.ITALIC;

    private static final String TAG = "WebvttCueParser";

    private final StringBuilder textBuilder;

    public WebvttCueParser() {
        textBuilder = new StringBuilder();
    }

    /**
     * Parses the next valid WebVTT cue in a parsable array, including timestamps, settings and text.
     *
     * @param webvttData Parsable WebVTT file data.
     * @param builder    Builder for WebVTT Cues.
     * @param styles     List of styles defined by the CSS style blocks preceeding the cues.
     * @return Whether a valid Cue was found.
     */
    public boolean parseCue(ParsableByteArray webvttData, WebvttCue.Builder builder,
                            List<WebvttCssStyle> styles) {
        String firstLine = webvttData.readLine();
        if (firstLine == null) {
            return false;
        }
        Matcher cueHeaderMatcher = com.google.android.exoplayer2.text.webvtt.WebvttCueParser.CUE_HEADER_PATTERN.matcher(firstLine);
        if (cueHeaderMatcher.matches()) {
            // We have found the timestamps in the first line. No id present.
            return parseCue(null, cueHeaderMatcher, webvttData, builder, textBuilder, styles);
        }
        // The first line is not the timestamps, but could be the cue id.
        String secondLine = webvttData.readLine();
        if (secondLine == null) {
            return false;
        }
        cueHeaderMatcher = com.google.android.exoplayer2.text.webvtt.WebvttCueParser.CUE_HEADER_PATTERN.matcher(secondLine);
        if (cueHeaderMatcher.matches()) {
            // We can do the rest of the parsing, including the id.
            return parseCue(firstLine.trim(), cueHeaderMatcher, webvttData, builder, textBuilder,
                            styles
            );
        }
        return false;
    }

    /**
     * Parses a string containing a list of cue settings.
     *
     * @param cueSettingsList String containing the settings for a given cue.
     * @param builder         The {@link WebvttCue.Builder} where incremental construction takes place.
     */
  /* package */
    static void parseCueSettingsList(String cueSettingsList,
                                     WebvttCue.Builder builder) {
        // Parse the cue settings list.
        Matcher cueSettingMatcher = CUE_SETTING_PATTERN.matcher(cueSettingsList);
        while (cueSettingMatcher.find()) {
            String name = cueSettingMatcher.group(1);
            String value = cueSettingMatcher.group(2);
            try {
                if ("line".equals(name)) {
                    parseLineAttribute(value, builder);
                } else if ("align".equals(name)) {
                    builder.setTextAlignment(parseTextAlignment(value));
                } else if ("position".equals(name)) {
                    parsePositionAttribute(value, builder);
                } else if ("size".equals(name)) {
                    builder.setWidth(WebvttParserUtil.parsePercentage(value));
                } else {
                    Log.w(TAG, "Unknown cue setting " + name + ":" + value);
                }
            } catch (NumberFormatException e) {
                Log.w(TAG, "Skipping bad cue setting: " + cueSettingMatcher.group());
            }
        }
    }

    /**
     * Parses the text payload of a WebVTT Cue and applies modifications on {@link WebvttCue.Builder}.
     *
     * @param id      Id of the cue, {@code null} if it is not present.
     * @param markup  The markup text to be parsed.
     * @param styles  List of styles defined by the CSS style blocks preceeding the cues.
     * @param builder Output builder.
     */
  /* package */
    static void parseCueText(String id, String markup, WebvttCue.Builder builder,
                             List<WebvttCssStyle> styles) {
        SpannableStringBuilder spannedText = new SpannableStringBuilder();
        Stack<StartTag> startTagStack = new Stack<>();
        List<StyleMatch> scratchStyleMatches = new ArrayList<>();
        int pos = 0;
        while (pos < markup.length()) {
            char curr = markup.charAt(pos);
            switch (curr) {
                case CHAR_LESS_THAN:
                    if (pos + 1 >= markup.length()) {
                        pos++;
                        break; // avoid ArrayOutOfBoundsException
                    }
                    int ltPos = pos;
                    boolean isClosingTag = markup.charAt(ltPos + 1) == CHAR_SLASH;
                    pos = findEndOfTag(markup, ltPos + 1);
                    boolean isVoidTag = markup.charAt(pos - 2) == CHAR_SLASH;
                    String fullTagExpression = markup.substring(
                        ltPos + (isClosingTag ? 2 : 1),
                        isVoidTag ? pos - 2 : pos - 1
                    );
                    String tagName = getTagName(fullTagExpression);
                    if (tagName == null || !isSupportedTag(tagName)) {
                        continue;
                    }
                    if (isClosingTag) {
                        StartTag startTag;
                        do {
                            if (startTagStack.isEmpty()) {
                                break;
                            }
                            startTag = startTagStack.pop();
                            applySpansForTag(id, startTag, spannedText, styles, scratchStyleMatches);
                        } while (!startTag.name.equals(tagName));
                    } else if (!isVoidTag) {
                        startTagStack.push(StartTag.buildStartTag(fullTagExpression, spannedText.length()));
                    }
                    break;
                case CHAR_AMPERSAND:
                    int semiColonEndIndex = markup.indexOf(CHAR_SEMI_COLON, pos + 1);
                    int spaceEndIndex = markup.indexOf(CHAR_SPACE, pos + 1);
                    int entityEndIndex = semiColonEndIndex == -1 ? spaceEndIndex
                        : (spaceEndIndex == -1 ? semiColonEndIndex
                        : Math.min(semiColonEndIndex, spaceEndIndex));
                    if (entityEndIndex != -1) {
                        applyEntity(markup.substring(pos + 1, entityEndIndex), spannedText);
                        if (entityEndIndex == spaceEndIndex) {
                            spannedText.append(" ");
                        }
                        pos = entityEndIndex + 1;
                    } else {
                        spannedText.append(curr);
                        pos++;
                    }
                    break;
                default:
                    spannedText.append(curr);
                    pos++;
                    break;
            }
        }
        // apply unclosed tags
        while (!startTagStack.isEmpty()) {
            applySpansForTag(id, startTagStack.pop(), spannedText, styles, scratchStyleMatches);
        }
        applySpansForTag(id, StartTag.buildWholeCueVirtualTag(), spannedText, styles,
                         scratchStyleMatches
        );
        builder.setText(spannedText);
    }

    private static boolean parseCue(String id, Matcher cueHeaderMatcher, ParsableByteArray webvttData,
                                    WebvttCue.Builder builder, StringBuilder textBuilder, List<WebvttCssStyle> styles) {
        try {
            // Parse the cue start and end times.
            builder.setStartTime(WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(1)))
                .setEndTime(WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(2)));
        } catch (NumberFormatException e) {
            Log.w(TAG, "Skipping cue with bad header: " + cueHeaderMatcher.group());
            return false;
        }

        parseCueSettingsList(cueHeaderMatcher.group(3), builder);

        // Parse the cue text.
        textBuilder.setLength(0);
        String line;
        while (!TextUtils.isEmpty(line = webvttData.readLine())) {
            if (textBuilder.length() > 0) {
                textBuilder.append("\n");
            }
            textBuilder.append(line.trim());
        }
        parseCueText(id, textBuilder.toString(), builder, styles);
        return true;
    }

    // Internal methods

    private static void parseLineAttribute(String s, WebvttCue.Builder builder)
        throws NumberFormatException {
        int commaIndex = s.indexOf(',');
        if (commaIndex != -1) {
            builder.setLineAnchor(parsePositionAnchor(s.substring(commaIndex + 1)));
            s = s.substring(0, commaIndex);
        } else {
            builder.setLineAnchor(Cue.TYPE_UNSET);
        }
        if (s.endsWith("%")) {
            builder.setLine(WebvttParserUtil.parsePercentage(s)).setLineType(Cue.LINE_TYPE_FRACTION);
        } else {
            int lineNumber = Integer.parseInt(s);
            if (lineNumber < 0) {
                // WebVTT defines line -1 as last visible row when lineAnchor is ANCHOR_TYPE_START, where-as
                // Cue defines it to be the first row that's not visible.
                lineNumber--;
            }
            builder.setLine(lineNumber).setLineType(Cue.LINE_TYPE_NUMBER);
        }
    }

    private static void parsePositionAttribute(String s, WebvttCue.Builder builder)
        throws NumberFormatException {
        int commaIndex = s.indexOf(',');
        if (commaIndex != -1) {
            builder.setPositionAnchor(parsePositionAnchor(s.substring(commaIndex + 1)));
            s = s.substring(0, commaIndex);
        } else {
            builder.setPositionAnchor(Cue.TYPE_UNSET);
        }
        builder.setPosition(WebvttParserUtil.parsePercentage(s));
    }

    private static int parsePositionAnchor(String s) {
        switch (s) {
            case "start":
                return Cue.ANCHOR_TYPE_START;
            case "center":
            case "middle":
                return Cue.ANCHOR_TYPE_MIDDLE;
            case "end":
                return Cue.ANCHOR_TYPE_END;
            default:
                Log.w(TAG, "Invalid anchor value: " + s);
                return Cue.TYPE_UNSET;
        }
    }

    private static Alignment parseTextAlignment(String s) {
        switch (s) {
            case "start":
            case "left":
                return Alignment.ALIGN_NORMAL;
            case "center":
            case "middle":
                return Alignment.ALIGN_CENTER;
            case "end":
            case "right":
                return Alignment.ALIGN_OPPOSITE;
            default:
                Log.w(TAG, "Invalid alignment value: " + s);
                return null;
        }
    }

    /**
     * Find end of tag (&gt;). The position returned is the position of the &gt; plus one (exclusive).
     *
     * @param markup   The WebVTT cue markup to be parsed.
     * @param startPos The position from where to start searching for the end of tag.
     * @return The position of the end of tag plus 1 (one).
     */
    private static int findEndOfTag(String markup, int startPos) {
        int index = markup.indexOf(CHAR_GREATER_THAN, startPos);
        return index == -1 ? markup.length() : index + 1;
    }

    private static void applyEntity(String entity, SpannableStringBuilder spannedText) {
        switch (entity) {
            case ENTITY_LESS_THAN:
                spannedText.append('<');
                break;
            case ENTITY_GREATER_THAN:
                spannedText.append('>');
                break;
            case ENTITY_NON_BREAK_SPACE:
                spannedText.append(' ');
                break;
            case ENTITY_AMPERSAND:
                spannedText.append('&');
                break;
            default:
                Log.w(TAG, "ignoring unsupported entity: '&" + entity + ";'");
                break;
        }
    }

    private static boolean isSupportedTag(String tagName) {
        switch (tagName) {
            case TAG_BOLD:
            case TAG_CLASS:
            case TAG_ITALIC:
            case TAG_LANG:
            case TAG_UNDERLINE:
            case TAG_VOICE:
                return true;
            default:
                return false;
        }
    }

    private static void applySpansForTag(String cueId, StartTag startTag, SpannableStringBuilder text,
                                         List<WebvttCssStyle> styles, List<StyleMatch> scratchStyleMatches) {
        int start = startTag.position;
        int end = text.length();
        switch (startTag.name) {
            case TAG_BOLD:
                text.setSpan(new StyleSpan(STYLE_BOLD), start, end,
                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                );
                break;
            case TAG_ITALIC:
                text.setSpan(new StyleSpan(STYLE_ITALIC), start, end,
                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                );
                break;
            case TAG_UNDERLINE:
                text.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                break;
            case TAG_CLASS:
                applySupportedClasses(text, startTag.classes, start, end);
                break;
            case TAG_LANG:
            case TAG_VOICE:
            case "": // Case of the "whole cue" virtual tag.
                break;
            default:
                return;
        }
        scratchStyleMatches.clear();
        getApplicableStyles(styles, cueId, startTag, scratchStyleMatches);
        int styleMatchesCount = scratchStyleMatches.size();
        for (int i = 0; i < styleMatchesCount; i++) {
            applyStyleToText(text, scratchStyleMatches.get(i).style, start, end);
        }
    }

    private static void applySupportedClasses(SpannableStringBuilder text, String[] classes,
                                              int start, int end) {
        for (String className : classes) {
            if (ColorParser.isNamedColor(className)) {
                int color = ColorParser.parseCssColor(className);
                text.setSpan(new ForegroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }

    private static void applyStyleToText(SpannableStringBuilder spannedText, WebvttCssStyle style,
                                         int start, int end) {
        if (style == null) {
            return;
        }
        if (style.getStyle() != WebvttCssStyle.UNSPECIFIED) {
            spannedText.setSpan(new StyleSpan(style.getStyle()), start, end,
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
        if (style.isLinethrough()) {
            spannedText.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        if (style.isUnderline()) {
            spannedText.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        if (style.hasFontColor()) {
            spannedText.setSpan(new ForegroundColorSpan(style.getFontColor()), start, end,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
        if (style.hasBackgroundColor()) {
            spannedText.setSpan(new BackgroundColorSpan(style.getBackgroundColor()), start, end,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
        if (style.getFontFamily() != null) {
            spannedText.setSpan(new TypefaceSpan(style.getFontFamily()), start, end,
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
        if (style.getTextAlign() != null) {
            spannedText.setSpan(new AlignmentSpan.Standard(style.getTextAlign()), start, end,
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
        switch (style.getFontSizeUnit()) {
            case WebvttCssStyle.FONT_SIZE_UNIT_PIXEL:
                spannedText.setSpan(new AbsoluteSizeSpan((int) style.getFontSize(), true), start, end,
                                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                );
                break;
            case WebvttCssStyle.FONT_SIZE_UNIT_EM:
                spannedText.setSpan(new RelativeSizeSpan(style.getFontSize()), start, end,
                                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                );
                break;
            case WebvttCssStyle.FONT_SIZE_UNIT_PERCENT:
                spannedText.setSpan(new RelativeSizeSpan(style.getFontSize() / 100), start, end,
                                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                );
                break;
            case WebvttCssStyle.UNSPECIFIED:
                // Do nothing.
                break;
        }
    }

    /**
     * Returns the tag name for the given tag contents.
     *
     * @param tagExpression Characters between &amp;lt: and &amp;gt; of a start or end tag.
     * @return The name of tag.
     */
    private static String getTagName(String tagExpression) {
        tagExpression = tagExpression.trim();
        if (tagExpression.isEmpty()) {
            return null;
        }
        return tagExpression.split("[ \\.]")[0];
    }

    private static void getApplicableStyles(List<WebvttCssStyle> declaredStyles, String id,
                                            StartTag tag, List<StyleMatch> output) {
        int styleCount = declaredStyles.size();
        for (int i = 0; i < styleCount; i++) {
            WebvttCssStyle style = declaredStyles.get(i);
            int score = style.getSpecificityScore(id, tag.name, tag.classes, tag.voice);
            if (score > 0) {
                output.add(new StyleMatch(score, style));
            }
        }
        Collections.sort(output);
    }

    private static final class StyleMatch implements Comparable<StyleMatch> {

        public final int score;
        public final WebvttCssStyle style;

        public StyleMatch(int score, WebvttCssStyle style) {
            this.score = score;
            this.style = style;
        }

        @Override
        public int compareTo(@NonNull StyleMatch another) {
            return this.score - another.score;
        }

    }

    private static final class StartTag {

        private static final String[] NO_CLASSES = new String[0];

        public final String name;
        public final int position;
        public final String voice;
        public final String[] classes;

        private StartTag(String name, int position, String voice, String[] classes) {
            this.position = position;
            this.name = name;
            this.voice = voice;
            this.classes = classes;
        }

        public static StartTag buildStartTag(String fullTagExpression, int position) {
            fullTagExpression = fullTagExpression.trim();
            if (fullTagExpression.isEmpty()) {
                return null;
            }
            int voiceStartIndex = fullTagExpression.indexOf(" ");
            String voice;
            if (voiceStartIndex == -1) {
                voice = "";
            } else {
                voice = fullTagExpression.substring(voiceStartIndex).trim();
                fullTagExpression = fullTagExpression.substring(0, voiceStartIndex);
            }
            String[] nameAndClasses = fullTagExpression.split("\\.");
            String name = nameAndClasses[0];
            String[] classes;
            if (nameAndClasses.length > 1) {
                classes = Arrays.copyOfRange(nameAndClasses, 1, nameAndClasses.length);
            } else {
                classes = NO_CLASSES;
            }
            return new StartTag(name, position, voice, classes);
        }

        public static StartTag buildWholeCueVirtualTag() {
            return new StartTag("", 0, "", new String[0]);
        }

    }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttDecoder.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.novoda.noplayer.external.exoplayer.text.webvtt;

import android.text.TextUtils;

import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.text.webvtt.WebvttCssStyle;
import com.google.android.exoplayer2.text.webvtt.WebvttCue;
import com.google.android.exoplayer2.text.webvtt.WebvttParserUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;

import java.util.ArrayList;
import java.util.List;

/**
 * A {@link SimpleSubtitleDecoder} for WebVTT.
 * <p>
 * @see <a href="http://dev.w3.org/html5/webvtt">WebVTT specification</a>
 */
public final class WebvttDecoder extends SimpleSubtitleDecoder {

  private static final int EVENT_NONE = -1;
  private static final int EVENT_END_OF_FILE = 0;
  private static final int EVENT_COMMENT = 1;
  private static final int EVENT_STYLE_BLOCK = 2;
  private static final int EVENT_CUE = 3;

  private static final String COMMENT_START = "NOTE";
  private static final String STYLE_START = "STYLE";

  private final WebvttCueParser cueParser;
  private final ParsableByteArray parsableWebvttData;
  private final WebvttCue.Builder webvttCueBuilder;
  private final CssParser cssParser;
  private final List<WebvttCssStyle> definedStyles;

  public WebvttDecoder() {
    super("WebvttDecoder");
    cueParser = new WebvttCueParser();
    parsableWebvttData = new ParsableByteArray();
    webvttCueBuilder = new WebvttCue.Builder();
    cssParser = new CssParser();
    definedStyles = new ArrayList<>();
  }

  @Override
  protected WebvttSubtitle decode(byte[] bytes, int length, boolean reset)
      throws SubtitleDecoderException {
    parsableWebvttData.reset(bytes, length);
    // Initialization for consistent starting state.
    webvttCueBuilder.reset();
    definedStyles.clear();

    // Validate the first line of the header, and skip the remainder.
    try {
      WebvttParserUtil.validateWebvttHeaderLine(parsableWebvttData);
    } catch (ParserException e) {
      throw new SubtitleDecoderException(e);
    }
    while (!TextUtils.isEmpty(parsableWebvttData.readLine())) {
    }

    int event;
    ArrayList<WebvttCue> subtitles = new ArrayList<>();
    while ((event = getNextEvent(parsableWebvttData)) != EVENT_END_OF_FILE) {
      if (event == EVENT_COMMENT) {
        skipComment(parsableWebvttData);
      } else if (event == EVENT_STYLE_BLOCK) {
        if (!subtitles.isEmpty()) {
          throw new SubtitleDecoderException("A style block was found after the first cue.");
        }
        parsableWebvttData.readLine(); // Consume the "STYLE" header.
        WebvttCssStyle styleBlock = cssParser.parseBlock(parsableWebvttData);
        if (styleBlock != null) {
          definedStyles.add(styleBlock);
        }
      } else if (event == EVENT_CUE) {
        if (cueParser.parseCue(parsableWebvttData, webvttCueBuilder, definedStyles)) {
          subtitles.add(webvttCueBuilder.build());
          webvttCueBuilder.reset();
        }
      }
    }
    return new WebvttSubtitle(subtitles);
  }

  /**
   * Positions the input right before the next event, and returns the kind of event found. Does not
   * consume any data from such event, if any.
   *
   * @return The kind of event found.
   */
  private static int getNextEvent(ParsableByteArray parsableWebvttData) {
    int foundEvent = EVENT_NONE;
    int currentInputPosition = 0;
    while (foundEvent == EVENT_NONE) {
      currentInputPosition = parsableWebvttData.getPosition();
      String line = parsableWebvttData.readLine();
      if (line == null) {
        foundEvent = EVENT_END_OF_FILE;
      } else if (STYLE_START.equals(line)) {
        foundEvent = EVENT_STYLE_BLOCK;
      } else if (COMMENT_START.startsWith(line)) {
        foundEvent = EVENT_COMMENT;
      } else {
        foundEvent = EVENT_CUE;
      }
    }
    parsableWebvttData.setPosition(currentInputPosition);
    return foundEvent;
  }

  private static void skipComment(ParsableByteArray parsableWebvttData) {
    while (!TextUtils.isEmpty(parsableWebvttData.readLine())) {}
  }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttSubtitle.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.novoda.noplayer.external.exoplayer.text.webvtt;

import android.text.SpannableStringBuilder;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.text.webvtt.WebvttCue;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * A representation of a WebVTT subtitle.
 */
/* package */ final class WebvttSubtitle implements Subtitle {

  private final List<WebvttCue> cues;
  private final int numCues;
  private final long[] cueTimesUs;
  private final long[] sortedCueTimesUs;

  /**
   * @param cues A list of the cues in this subtitle.
   */
  public WebvttSubtitle(List<WebvttCue> cues) {
    this.cues = cues;
    numCues = cues.size();
    cueTimesUs = new long[2 * numCues];
    for (int cueIndex = 0; cueIndex < numCues; cueIndex++) {
      WebvttCue cue = cues.get(cueIndex);
      int arrayIndex = cueIndex * 2;
      cueTimesUs[arrayIndex] = cue.startTime;
      cueTimesUs[arrayIndex + 1] = cue.endTime;
    }
    sortedCueTimesUs = Arrays.copyOf(cueTimesUs, cueTimesUs.length);
    Arrays.sort(sortedCueTimesUs);
  }

  @Override
  public int getNextEventTimeIndex(long timeUs) {
    int index = Util.binarySearchCeil(sortedCueTimesUs, timeUs, false, false);
    return index < sortedCueTimesUs.length ? index : C.INDEX_UNSET;
  }

  @Override
  public int getEventTimeCount() {
    return sortedCueTimesUs.length;
  }

  @Override
  public long getEventTime(int index) {
    Assertions.checkArgument(index >= 0);
    Assertions.checkArgument(index < sortedCueTimesUs.length);
    return sortedCueTimesUs[index];
  }

  @Override
  public List<Cue> getCues(long timeUs) {
    ArrayList<Cue> list = null;
    WebvttCue firstNormalCue = null;
    SpannableStringBuilder normalCueTextBuilder = null;

    for (int i = 0; i < numCues; i++) {
      if ((cueTimesUs[i * 2] <= timeUs) && (timeUs < cueTimesUs[i * 2 + 1])) {
        if (list == null) {
          list = new ArrayList<>();
        }
        WebvttCue cue = cues.get(i);
        if (cue.isNormalCue()) {
          // we want to merge all of the normal cues into a single cue to ensure they are drawn
          // correctly (i.e. don't overlap) and to emulate roll-up, but only if there are multiple
          // normal cues, otherwise we can just append the single normal cue
          if (firstNormalCue == null) {
            firstNormalCue = cue;
          } else if (normalCueTextBuilder == null) {
            normalCueTextBuilder = new SpannableStringBuilder();
            normalCueTextBuilder.append(firstNormalCue.text).append("\n").append(cue.text);
          } else {
            normalCueTextBuilder.append("\n").append(cue.text);
          }
        } else {
          list.add(cue);
        }
      }
    }
    if (normalCueTextBuilder != null) {
      // there were multiple normal cues, so create a new cue with all of the text
      list.add(new WebvttCue(normalCueTextBuilder));
    } else if (firstNormalCue != null) {
      // there was only a single normal cue, so just add it to the list
      list.add(firstNormalCue);
    }

    if (list != null) {
      return list;
    } else {
      return Collections.emptyList();
    }
  }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/util/ColorParser.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.novoda.noplayer.external.exoplayer.util;

import android.text.TextUtils;

import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Parser for color expressions found in styling formats, e.g. TTML and CSS.
 *
 * @see <a href="https://w3c.github.io/webvtt/#styling">WebVTT CSS Styling</a>
 * @see <a href="https://www.w3.org/TR/ttml2/">Timed Text Markup Language 2 (TTML2) - 10.3.5</a>
 **/
public final class ColorParser {

  private static final String RGB = "rgb";
  private static final String RGBA = "rgba";

  private static final Pattern RGB_PATTERN = Pattern.compile(
      "^rgb\\((\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)$");

  private static final Pattern RGBA_PATTERN_INT_ALPHA = Pattern.compile(
      "^rgba\\((\\d{1,3}),(\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)$");

  private static final Pattern RGBA_PATTERN_FLOAT_ALPHA = Pattern.compile(
      "^rgba\\((\\d{1,3}),(\\d{1,3}),(\\d{1,3}),(\\d*\\.?\\d*?)\\)$");

  private static final Map<String, Integer> COLOR_MAP;

  public static boolean isNamedColor(String expression) {
    return COLOR_MAP.containsKey(expression);
  }

  /**
   * Parses a TTML color expression.
   *
   * @param colorExpression The color expression.
   * @return The parsed ARGB color.
   */
  public static int parseTtmlColor(String colorExpression) {
    return parseColorInternal(colorExpression, false);
  }

  /**
   * Parses a CSS color expression.
   *
   * @param colorExpression The color expression.
   * @return The parsed ARGB color.
   */
  public static int parseCssColor(String colorExpression) {
    return parseColorInternal(colorExpression, true);
  }

  private static int parseColorInternal(String colorExpression, boolean alphaHasFloatFormat) {
    Assertions.checkArgument(!TextUtils.isEmpty(colorExpression));
    colorExpression = colorExpression.replace(" ", "");
    if (colorExpression.charAt(0) == '#') {
      // Parse using Long to avoid failure when colorExpression is greater than #7FFFFFFF.
      int color = (int) Long.parseLong(colorExpression.substring(1), 16);
      if (colorExpression.length() == 7) {
        // Set the alpha value
        color |= 0xFF000000;
      } else if (colorExpression.length() == 9) {
        // We have #RRGGBBAA, but we need #AARRGGBB
        color = ((color & 0xFF) << 24) | (color >>> 8);
      } else {
        throw new IllegalArgumentException();
      }
      return color;
    } else if (colorExpression.startsWith(RGBA)) {
      Matcher matcher = (alphaHasFloatFormat ? RGBA_PATTERN_FLOAT_ALPHA : RGBA_PATTERN_INT_ALPHA)
          .matcher(colorExpression);
      if (matcher.matches()) {
        return argb(
          alphaHasFloatFormat ? (int) (255 * Float.parseFloat(matcher.group(4)))
              : Integer.parseInt(matcher.group(4), 10),
          Integer.parseInt(matcher.group(1), 10),
          Integer.parseInt(matcher.group(2), 10),
          Integer.parseInt(matcher.group(3), 10)
        );
      }
    } else if (colorExpression.startsWith(RGB)) {
      Matcher matcher = RGB_PATTERN.matcher(colorExpression);
      if (matcher.matches()) {
        return rgb(
          Integer.parseInt(matcher.group(1), 10),
          Integer.parseInt(matcher.group(2), 10),
          Integer.parseInt(matcher.group(3), 10)
        );
      }
    } else {
      // we use our own color map
      Integer color = COLOR_MAP.get(Util.toLowerInvariant(colorExpression));
      if (color != null) {
        return color;
      }
    }
    throw new IllegalArgumentException();
  }

  private static int argb(int alpha, int red, int green, int blue) {
    return (alpha << 24) | (red << 16) | (green << 8) | blue;
  }

  private static int rgb(int red, int green, int blue) {
    return argb(0xFF, red, green, blue);
  }

  static {
    COLOR_MAP = new HashMap<>();
    COLOR_MAP.put("aliceblue", 0xFFF0F8FF);
    COLOR_MAP.put("antiquewhite", 0xFFFAEBD7);
    COLOR_MAP.put("aqua", 0xFF00FFFF);
    COLOR_MAP.put("aquamarine", 0xFF7FFFD4);
    COLOR_MAP.put("azure", 0xFFF0FFFF);
    COLOR_MAP.put("beige", 0xFFF5F5DC);
    COLOR_MAP.put("bisque", 0xFFFFE4C4);
    COLOR_MAP.put("black", 0xFF000000);
    COLOR_MAP.put("blanchedalmond", 0xFFFFEBCD);
    COLOR_MAP.put("blue", 0xFF0000FF);
    COLOR_MAP.put("blueviolet", 0xFF8A2BE2);
    COLOR_MAP.put("brown", 0xFFA52A2A);
    COLOR_MAP.put("burlywood", 0xFFDEB887);
    COLOR_MAP.put("cadetblue", 0xFF5F9EA0);
    COLOR_MAP.put("chartreuse", 0xFF7FFF00);
    COLOR_MAP.put("chocolate", 0xFFD2691E);
    COLOR_MAP.put("coral", 0xFFFF7F50);
    COLOR_MAP.put("cornflowerblue", 0xFF6495ED);
    COLOR_MAP.put("cornsilk", 0xFFFFF8DC);
    COLOR_MAP.put("crimson", 0xFFDC143C);
    COLOR_MAP.put("cyan", 0xFF00FFFF);
    COLOR_MAP.put("darkblue", 0xFF00008B);
    COLOR_MAP.put("darkcyan", 0xFF008B8B);
    COLOR_MAP.put("darkgoldenrod", 0xFFB8860B);
    COLOR_MAP.put("darkgray", 0xFFA9A9A9);
    COLOR_MAP.put("darkgreen", 0xFF006400);
    COLOR_MAP.put("darkgrey", 0xFFA9A9A9);
    COLOR_MAP.put("darkkhaki", 0xFFBDB76B);
    COLOR_MAP.put("darkmagenta", 0xFF8B008B);
    COLOR_MAP.put("darkolivegreen", 0xFF556B2F);
    COLOR_MAP.put("darkorange", 0xFFFF8C00);
    COLOR_MAP.put("darkorchid", 0xFF9932CC);
    COLOR_MAP.put("darkred", 0xFF8B0000);
    COLOR_MAP.put("darksalmon", 0xFFE9967A);
    COLOR_MAP.put("darkseagreen", 0xFF8FBC8F);
    COLOR_MAP.put("darkslateblue", 0xFF483D8B);
    COLOR_MAP.put("darkslategray", 0xFF2F4F4F);
    COLOR_MAP.put("darkslategrey", 0xFF2F4F4F);
    COLOR_MAP.put("darkturquoise", 0xFF00CED1);
    COLOR_MAP.put("darkviolet", 0xFF9400D3);
    COLOR_MAP.put("deeppink", 0xFFFF1493);
    COLOR_MAP.put("deepskyblue", 0xFF00BFFF);
    COLOR_MAP.put("dimgray", 0xFF696969);
    COLOR_MAP.put("dimgrey", 0xFF696969);
    COLOR_MAP.put("dodgerblue", 0xFF1E90FF);
    COLOR_MAP.put("firebrick", 0xFFB22222);
    COLOR_MAP.put("floralwhite", 0xFFFFFAF0);
    COLOR_MAP.put("forestgreen", 0xFF228B22);
    COLOR_MAP.put("fuchsia", 0xFFFF00FF);
    COLOR_MAP.put("gainsboro", 0xFFDCDCDC);
    COLOR_MAP.put("ghostwhite", 0xFFF8F8FF);
    COLOR_MAP.put("gold", 0xFFFFD700);
    COLOR_MAP.put("goldenrod", 0xFFDAA520);
    COLOR_MAP.put("gray", 0xFF808080);
    COLOR_MAP.put("green", 0xFF008000);
    COLOR_MAP.put("greenyellow", 0xFFADFF2F);
    COLOR_MAP.put("grey", 0xFF808080);
    COLOR_MAP.put("honeydew", 0xFFF0FFF0);
    COLOR_MAP.put("hotpink", 0xFFFF69B4);
    COLOR_MAP.put("indianred", 0xFFCD5C5C);
    COLOR_MAP.put("indigo", 0xFF4B0082);
    COLOR_MAP.put("ivory", 0xFFFFFFF0);
    COLOR_MAP.put("khaki", 0xFFF0E68C);
    COLOR_MAP.put("lavender", 0xFFE6E6FA);
    COLOR_MAP.put("lavenderblush", 0xFFFFF0F5);
    COLOR_MAP.put("lawngreen", 0xFF7CFC00);
    COLOR_MAP.put("lemonchiffon", 0xFFFFFACD);
    COLOR_MAP.put("lightblue", 0xFFADD8E6);
    COLOR_MAP.put("lightcoral", 0xFFF08080);
    COLOR_MAP.put("lightcyan", 0xFFE0FFFF);
    COLOR_MAP.put("lightgoldenrodyellow", 0xFFFAFAD2);
    COLOR_MAP.put("lightgray", 0xFFD3D3D3);
    COLOR_MAP.put("lightgreen", 0xFF90EE90);
    COLOR_MAP.put("lightgrey", 0xFFD3D3D3);
    COLOR_MAP.put("lightpink", 0xFFFFB6C1);
    COLOR_MAP.put("lightsalmon", 0xFFFFA07A);
    COLOR_MAP.put("lightseagreen", 0xFF20B2AA);
    COLOR_MAP.put("lightskyblue", 0xFF87CEFA);
    COLOR_MAP.put("lightslategray", 0xFF778899);
    COLOR_MAP.put("lightslategrey", 0xFF778899);
    COLOR_MAP.put("lightsteelblue", 0xFFB0C4DE);
    COLOR_MAP.put("lightyellow", 0xFFFFFFE0);
    COLOR_MAP.put("lime", 0xFF00FF00);
    COLOR_MAP.put("limegreen", 0xFF32CD32);
    COLOR_MAP.put("linen", 0xFFFAF0E6);
    COLOR_MAP.put("magenta", 0xFFFF00FF);
    COLOR_MAP.put("maroon", 0xFF800000);
    COLOR_MAP.put("mediumaquamarine", 0xFF66CDAA);
    COLOR_MAP.put("mediumblue", 0xFF0000CD);
    COLOR_MAP.put("mediumorchid", 0xFFBA55D3);
    COLOR_MAP.put("mediumpurple", 0xFF9370DB);
    COLOR_MAP.put("mediumseagreen", 0xFF3CB371);
    COLOR_MAP.put("mediumslateblue", 0xFF7B68EE);
    COLOR_MAP.put("mediumspringgreen", 0xFF00FA9A);
    COLOR_MAP.put("mediumturquoise", 0xFF48D1CC);
    COLOR_MAP.put("mediumvioletred", 0xFFC71585);
    COLOR_MAP.put("midnightblue", 0xFF191970);
    COLOR_MAP.put("mintcream", 0xFFF5FFFA);
    COLOR_MAP.put("mistyrose", 0xFFFFE4E1);
    COLOR_MAP.put("moccasin", 0xFFFFE4B5);
    COLOR_MAP.put("navajowhite", 0xFFFFDEAD);
    COLOR_MAP.put("navy", 0xFF000080);
    COLOR_MAP.put("oldlace", 0xFFFDF5E6);
    COLOR_MAP.put("olive", 0xFF808000);
    COLOR_MAP.put("olivedrab", 0xFF6B8E23);
    COLOR_MAP.put("orange", 0xFFFFA500);
    COLOR_MAP.put("orangered", 0xFFFF4500);
    COLOR_MAP.put("orchid", 0xFFDA70D6);
    COLOR_MAP.put("palegoldenrod", 0xFFEEE8AA);
    COLOR_MAP.put("palegreen", 0xFF98FB98);
    COLOR_MAP.put("paleturquoise", 0xFFAFEEEE);
    COLOR_MAP.put("palevioletred", 0xFFDB7093);
    COLOR_MAP.put("papayawhip", 0xFFFFEFD5);
    COLOR_MAP.put("peachpuff", 0xFFFFDAB9);
    COLOR_MAP.put("peru", 0xFFCD853F);
    COLOR_MAP.put("pink", 0xFFFFC0CB);
    COLOR_MAP.put("plum", 0xFFDDA0DD);
    COLOR_MAP.put("powderblue", 0xFFB0E0E6);
    COLOR_MAP.put("purple", 0xFF800080);
    COLOR_MAP.put("rebeccapurple", 0xFF663399);
    COLOR_MAP.put("red", 0xFFFF0000);
    COLOR_MAP.put("rosybrown", 0xFFBC8F8F);
    COLOR_MAP.put("royalblue", 0xFF4169E1);
    COLOR_MAP.put("saddlebrown", 0xFF8B4513);
    COLOR_MAP.put("salmon", 0xFFFA8072);
    COLOR_MAP.put("sandybrown", 0xFFF4A460);
    COLOR_MAP.put("seagreen", 0xFF2E8B57);
    COLOR_MAP.put("seashell", 0xFFFFF5EE);
    COLOR_MAP.put("sienna", 0xFFA0522D);
    COLOR_MAP.put("silver", 0xFFC0C0C0);
    COLOR_MAP.put("skyblue", 0xFF87CEEB);
    COLOR_MAP.put("slateblue", 0xFF6A5ACD);
    COLOR_MAP.put("slategray", 0xFF708090);
    COLOR_MAP.put("slategrey", 0xFF708090);
    COLOR_MAP.put("snow", 0xFFFFFAFA);
    COLOR_MAP.put("springgreen", 0xFF00FF7F);
    COLOR_MAP.put("steelblue", 0xFF4682B4);
    COLOR_MAP.put("tan", 0xFFD2B48C);
    COLOR_MAP.put("teal", 0xFF008080);
    COLOR_MAP.put("thistle", 0xFFD8BFD8);
    COLOR_MAP.put("tomato", 0xFFFF6347);
    COLOR_MAP.put("transparent", 0x00000000);
    COLOR_MAP.put("turquoise", 0xFF40E0D0);
    COLOR_MAP.put("violet", 0xFFEE82EE);
    COLOR_MAP.put("wheat", 0xFFF5DEB3);
    COLOR_MAP.put("white", 0xFFFFFFFF);
    COLOR_MAP.put("whitesmoke", 0xFFF5F5F5);
    COLOR_MAP.put("yellow", 0xFFFFFF00);
    COLOR_MAP.put("yellowgreen", 0xFF9ACD32);
  }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/Clock.java
================================================
package com.novoda.noplayer.internal;

import java.io.Serializable;

public interface Clock extends Serializable {

    long getCurrentTime();
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/Heart.java
================================================
package com.novoda.noplayer.internal;

import android.os.Handler;

import com.novoda.noplayer.NoPlayer;

@SuppressWarnings("checkstyle:FinalClass")  // We cannot make it final as we need to mock it in tests
public class Heart {

    private static final long HEART_BEAT_FREQUENCY_IN_MILLIS = 500;

    private final Handler handler;
    private final long heartbeatFrequency;

    private Heartbeat heartbeatAction;

    private boolean beating;

    public static Heart newInstance(Handler handler) {
        return new Heart(handler, HEART_BEAT_FREQUENCY_IN_MILLIS);
    }

    private Heart(Handler handler, long heartbeatFrequencyInMillis) {
        this.handler = handler;
        this.heartbeatFrequency = heartbeatFrequencyInMillis;
    }

    public void bind(Heartbeat onHeartbeat) {
        this.heartbeatAction = onHeartbeat;
    }

    public void startBeatingHeart() {
        if (heartbeatAction == null) {
            throw new IllegalStateException("You must call bind() with a valid non-null " + Heartbeat.class.getSimpleName());
        }
        stopBeatingHeart();
        beating = true;
        handler.post(heartbeat);
    }

    private final Runnable heartbeat = new Runnable() {
        @Override
        public void run() {
            handler.post(heartbeatAction);
            scheduleNextBeat();
        }
    };

    private void scheduleNextBeat() {
        handler.postDelayed(heartbeat, heartbeatFrequency);
    }

    public void stopBeatingHeart() {
        beating = false;
        handler.removeCallbacks(heartbeat);
    }

    public void forceBeat() {
        if (heartbeatAction == null) {
            throw new IllegalStateException("You must call bind() with a valid non-null " + Heartbeat.class.getSimpleName());
        }
        handler.post(heartbeatAction);
    }

    public boolean isBeating() {
        return beating;
    }

    public static class Heartbeat implements Runnable {

        private final NoPlayer.HeartbeatCallback callback;
        private final NoPlayer player;

        public Heartbeat(NoPlayer.HeartbeatCallback callback, NoPlayer player) {
            this.callback = callback;
            this.player = player;
        }

        @Override
        public void run() {
            if (player.isPlaying()) {
                callback.onBeat(player);
            }
        }
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/SystemClock.java
================================================
package com.novoda.noplayer.internal;

public class SystemClock implements Clock {

    @Override
    public long getCurrentTime() {
        return System.currentTimeMillis();
    }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/HttpPostingProvisionExecutor.java
================================================
package com.novoda.noplayer.internal.drm.provision;

import com.novoda.noplayer.drm.ModularDrmProvisionRequest;

import java.io.IOException;
import java.nio.charset.Charset;

class HttpPostingProvisionExecutor implements ProvisionExecutor {

    private static final String PARAMETER_SIGNED_REQUEST = "&signedRequest=";

    private final HttpUrlConnectionPoster httpPoster;
    private final ProvisioningCapabilities capabilities;

    HttpPostingProvisionExecutor(HttpUrlConnectionPoster httpPoster, ProvisioningCapabilities capabilities) {
        this.httpPoster = httpPoster;
        this.capabilities = capabilities;
    }

    @Override
    public byte[] execute(ModularDrmProvisionRequest request) throws IOException, UnableToProvisionException {
        if (isIncapableOfProvisioning()) {
            throw new UnableToProvisionException();
        }
        String provisioningUrl = buildProvisioningUrl(request);
        return httpPoster.post(provisioningUrl);
    }

    private boolean isIncapableOfProvisioning() {
        return !capabilities.canProvision();
    }

    private String buildProvisioningUrl(ModularDrmProvisionRequest request) {
        return request.url() + PARAMETER_SIGNED_REQUEST + new String(request.data(), Charset.forName("UTF-8"));
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/HttpUrlConnectionPoster.java
================================================
package com.novoda.noplayer.internal.drm.provision;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

class HttpUrlConnectionPoster {

    private static final String POST_REQUEST_METHOD = "POST";
    private static final int RESPONSE_BUFFER_SIZE = 1024 * 4;

    byte[] post(String url) throws IOException {
        HttpURLConnection urlConnection = null;
        try {
            urlConnection = (HttpURLConnection) new URL(url).openConnection();
            urlConnection.setRequestMethod(POST_REQUEST_METHOD);
            urlConnection.setDoInput(true);
            return byteArrayFrom(urlConnection);
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
    }

    private byte[] byteArrayFrom(HttpURLConnection urlConnection) throws IOException {
        InputStream inputStream = urlConnection.getInputStream();
        try {
            return byteArrayFrom(inputStream);
        } finally {
            inputStream.close();
        }
    }

    private byte[] byteArrayFrom(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[RESPONSE_BUFFER_SIZE];
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        try {
            return outputStream.toByteArray();
        } finally {
            outputStream.close();
        }
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisionExecutor.java
================================================
package com.novoda.noplayer.internal.drm.provision;

import com.novoda.noplayer.drm.ModularDrmProvisionRequest;

import java.io.IOException;

public interface ProvisionExecutor {

    byte[] execute(ModularDrmProvisionRequest request) throws IOException, UnableToProvisionException;
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisionExecutorCreator.java
================================================
package com.novoda.noplayer.internal.drm.provision;

public class ProvisionExecutorCreator {

    public ProvisionExecutor create() {
        HttpUrlConnectionPoster httpPoster = new HttpUrlConnectionPoster();
        ProvisioningCapabilities capabilities = ProvisioningCapabilities.newInstance();
        return new HttpPostingProvisionExecutor(httpPoster, capabilities);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisioningCapabilities.java
================================================
package com.novoda.noplayer.internal.drm.provision;

import android.os.Build;
import android.support.annotation.VisibleForTesting;

class ProvisioningCapabilities {

    private final int deviceOsVersion;

    static ProvisioningCapabilities newInstance() {
        return new ProvisioningCapabilities(Build.VERSION.SDK_INT);
    }

    @VisibleForTesting
    ProvisioningCapabilities(int deviceOsVersion) {
        this.deviceOsVersion = deviceOsVersion;
    }

    boolean canProvision() {
        return deviceOsVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2;
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/UnableToProvisionException.java
================================================
package com.novoda.noplayer.internal.drm.provision;

import android.os.Build;

public class UnableToProvisionException extends Exception {

    UnableToProvisionException() {
        super("Device is : " + Build.VERSION.SDK_INT
                + ", which is does not support provisioning, "
                + Build.VERSION_CODES.JELLY_BEAN_MR2
                + " and higher is required");
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/BandwidthMeterCreator.java
================================================
package com.novoda.noplayer.internal.exoplayer;

import android.content.Context;

import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;

class BandwidthMeterCreator {
    private final Context context;

    BandwidthMeterCreator(Context context) {
        this.context = context;
    }

    DefaultBandwidthMeter create(long maxInitialBitrate) {
        return new DefaultBandwidthMeter.Builder(context)
                .setInitialBitrateEstimate(maxInitialBitrate)
                .build();
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/CompositeTrackSelector.java
================================================
package com.novoda.noplayer.internal.exoplayer;

import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.novoda.noplayer.ContentType;
import com.novoda.noplayer.internal.exoplayer.mediasource.ExoPlayerAudioTrackSelector;
import com.novoda.noplayer.internal.exoplayer.mediasource.ExoPlayerSubtitleTrackSelector;
import com.novoda.noplayer.internal.exoplayer.mediasource.ExoPlayerVideoTrackSelector;
import com.novoda.noplayer.internal.utils.Optional;
import com.novoda.noplayer.model.AudioTracks;
import com.novoda.noplayer.model.PlayerAudioTrack;
import com.novoda.noplayer.model.PlayerSubtitleTrack;
import com.novoda.noplayer.model.PlayerVideoTrack;

import java.util.List;

class CompositeTrackSelector {

    private final DefaultTrackSelector defaultTrackSelector;
    private final ExoPlayerAudioTrackSelector audioTrackSelector;
    private final ExoPlayerVideoTrackSelector videoTrackSelector;
    private final ExoPlayerSubtitleTrackSelector subtitleTrackSelector;

    CompositeTrackSelector(DefaultTrackSelector defaultTrackSelector,
                           ExoPlayerAudioTrackSelector audioTrackSelector,
                           ExoPlayerVideoTrackSelector videoTrackSelector,
                           ExoPlayerSubtitleTrackSelector subtitleTrackSelector) {
        this.defaultTrackSelector = defaultTrackSelector;
        this.audioTrackSelector = audioTrackSelector;
        this.videoTrackSelector = videoTrackSelector;
        this.subtitleTrackSelector = subtitleTrackSelector;
    }

    TrackSelector trackSelector() {
        return defaultTrackSelector;
    }

    boolean selectAudioTrack(PlayerAudioTrack audioTrack, RendererTypeRequester rendererTypeRequester) {
        return audioTrackSelector.selectAudioTrack(audioTrack, rendererTypeRequester);
    }

    AudioTracks getAudioTracks(RendererTypeRequester rendererTypeRequester) {
        return audioTrackSelector.getAudioTracks(rendererTypeRequester);
    }

    boolean clearAudioTrack(RendererTypeRequester rendererTypeRequester) {
        return audioTrackSelector.clearAudioTrack(rendererTypeRequester);
    }

    boolean selectVideoTrack(PlayerVideoTrack videoTrack, RendererTypeRequester rendererTypeRequester) {
        return videoTrackSelector.selectVideoTrack(videoTrack, rendererTypeRequester);
    }

    List<PlayerVideoTrack> getVideoTracks(RendererTypeRequester rendererTypeRequester, ContentType contentType) {
        return videoTrackSelector.getVideoTracks(rendererTypeRequester, contentType);
    }

    Optional<PlayerVideoTrack> getSelectedVideoTrack(SimpleExoPlayer exoPlayer,
                                                     RendererTypeRequester rendererTypeRequester,
                                                     ContentType contentType) {
        return videoTrackSelector.getSelectedVideoTrack(exoPlayer, rendererTypeRequester, contentType);
    }

    boolean clearVideoTrack(RendererTypeRequester rendererTypeRequester) {
        return videoTrackSelector.clearVideoTrack(rendererTypeRequester);
    }

    boolean selectTextTrack(PlayerSubtitleTrack subtitleTrack, RendererTypeRequester rendererTypeRequester) {
        return subtitleTrackSelector.selectTextTrack(subtitleTrack, rendererTypeRequester);
    }

    List<PlayerSubtitleTrack> getSubtitleTracks(RendererTypeRequester rendererTypeRequester) {
        return subtitleTrackSelector.getSubtitleTracks(rendererTypeRequester);
    }

    boolean clearSubtitleTrack(RendererTypeRequester rendererTypeRequester) {
        return subtitleTrackSelector.clearSubtitleTrack(rendererTypeRequester);
    }

    void clearMaxVideoBitrate() {
        videoTrackSelector.clearMaxVideoBitrate();
    }

    void setMaxVideoBitrate(int maxVideoBitrate) {
        videoTrackSelector.setMaxVideoBitrate(maxVideoBitrate);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/CompositeTrackSelectorCreator.java
================================================
package com.novoda.noplayer.internal.exoplayer;

import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.Clock;
import com.novoda.noplayer.Options;
import com.novoda.noplayer.internal.exoplayer.mediasource.ExoPlayerAudioTrackSelector;
import com.novoda.noplayer.internal.exoplayer.mediasource.ExoPlayerSubtitleTrackSelector;
import com.novoda.noplayer.internal.exoplayer.mediasource.ExoPlayerTrackSelector;
import com.novoda.noplayer.internal.exoplayer.mediasource.ExoPlayerVideoTrackSelector;

class CompositeTrackSelectorCreator {

    CompositeTrackSelector create(Options options, DefaultBandwidthMeter bandwidthMeter) {
        TrackSelection.Factory adaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory(
                bandwidthMeter,
                options.minDurationBeforeQualityIncreaseInMillis(),
                AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
                AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
                AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION,
                AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE,
                AdaptiveTrackSelection.DEFAULT_MIN_TIME_BETWEEN_BUFFER_REEVALUTATION_MS,
                Clock.DEFAULT
        );
        DefaultTrackSelector trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);
        DefaultTrackSelector.Parameters trackSelectorParameters = trackSelector.buildUponParameters()
                .setMaxVideoBitrate(options.maxVideoBitrate())
                .build();
        trackSelector.setParameters(trackSelectorParameters);

        ExoPlayerTrackSelector exoPlayerTrackSelector = ExoPlayerTrackSelector.newInstance(trackSelector);
        ExoPlayerAudioTrackSelector audioTrackSelector = new ExoPlayerAudioTrackSelector(exoPlayerTrackSelector);
        ExoPlayerVideoTrackSelector videoTrackSelector = new ExoPlayerVideoTrackSelector(exoPlayerTrackSelector);
        ExoPlayerSubtitleTrackSelector subtitleTrackSelector = new ExoPlayerSubtitleTrackSelector(exoPlayerTrackSelector);
        return new CompositeTrackSelector(trackSelector, audioTrackSelector, videoTrackSelector, subtitleTrackSelector);
    }

}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerCreator.java
================================================
package com.novoda.noplayer.internal.exoplayer;

import android.content.Context;
import android.support.annotation.NonNull;

import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.text.SubtitleDecoderFactory;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.novoda.noplayer.internal.exoplayer.drm.DrmSessionCreator;
import com.novoda.noplayer.text.NoPlayerSubtitleDecoderFactory;

import static com.novoda.noplayer.internal.exoplayer.SimpleRenderersFactory.EXTENSION_RENDERER_MODE_OFF;

class ExoPlayerCreator {

    private static final long DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS = 5000;

    private final Context context;

    ExoPlayerCreator(Context context) {
        this.context = context;
    }

    @NonNull
    public SimpleExoPlayer create(DrmSessionCreator drmSessionCreator,
                                  DefaultDrmSessionEventListener drmSessionEventListener,
                                  MediaCodecSelector mediaCodecSelector,
                                  TrackSelector trackSelector) {
        DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = drmSessionCreator.create(drmSessionEventListener);
        SubtitleDecoderFactory subtitleDecoderFactory = new NoPlayerSubtitleDecoderFactory();
        RenderersFactory renderersFactory = new SimpleRenderersFactory(
                context,
                EXTENSION_RENDERER_MODE_OFF,
                DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS,
                mediaCodecSelector,
                subtitleDecoderFactory
        );

        DefaultLoadControl loadControl = new DefaultLoadControl();
        return ExoPlayerFactory.newSimpleInstance(context, renderersFactory, trackSelector, loadControl, drmSessionManager);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerCueMapper.java
================================================
package com.novoda.noplayer.internal.exoplayer;

import com.google.android.exoplayer2.text.Cue;
import com.novoda.noplayer.model.NoPlayerCue;
import com.novoda.noplayer.model.TextCues;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

final class ExoPlayerCueMapper {

    private ExoPlayerCueMapper() {
        // static class.
    }

    static TextCues map(List<Cue> cues) {
        if (cues == null) {
            return TextCues.of(Collections.<NoPlayerCue>emptyList());
        }

        List<NoPlayerCue> noPlayerCues = new ArrayList<>(cues.size());

        for (Cue cue : cues) {
            NoPlayerCue noPlayerCue = new NoPlayerCue(
                    cue.text,
                    cue.textAlignment,
                    cue.bitmap,
                    cue.line,
                    cue.lineType,
                    cue.lineAnchor,
                    cue.position,
                    cue.positionAnchor,
                    cue.size,
                    cue.bitmapHeight,
                    cue.windowColorSet,
                    cue.windowColor
            );
            noPlayerCues.add(noPlayerCue);
        }
        return TextCues.of(noPlayerCues);
    }
}


================================================
FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerFacade.java
================================================
package com.novoda.noplayer.internal.exoplayer;

import android.net.Uri;
import android.support.annotation.Nullable;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.novoda.noplayer.Options;
import com.novoda.noplayer.PlayerSurfaceHolder;
import com.novoda.noplayer.internal.exoplayer.drm.DrmSessionCreator;
import com.novoda.noplayer.internal.exoplayer.forwarder.ExoPlayerForwarder;
import com.novoda.noplayer.internal.exoplayer.mediasource.MediaSourceFactory;
import com.novoda.noplayer.internal.utils.AndroidDeviceVersion;
import com.novoda.noplayer.internal.utils.Optional;
import com.novoda.noplayer.model.AudioTracks;
import com.novoda.noplayer.model.PlayerAudioTrack;
import com.novoda.noplayer.model.PlayerSubtitleTrack;
import com.novoda.noplayer.model.PlayerVideoTrack;

import java.util.List;

class ExoPlayerFacade {

    private static final boolean DO_NOT_RESET_STATE = false;

    private final BandwidthMeterCreator bandwidthMeterCreator;
    private final AndroidDeviceVersion androidDeviceVersion;
    private final MediaSourceFactory mediaSourceFactory;
    private final CompositeTrackSelectorCreator trackSelectorCreator;
    private final ExoPlayerCreator exoPlayerCreator;
    private final RendererTypeRequesterCreator rendererTypeRequesterCreator;

    @Nullable
    private SimpleExoPlayer exoPlayer;
    @Nullable
    private CompositeTrackSelector compositeTrackSelector;
    @Nullable
    private RendererTypeRequester rendererTypeRequester;
    @Nullable
    private Options options;

    ExoPlayerFacade(BandwidthMeterCreator bandwidthMeterCreator,
                    AndroidDeviceVersion androidDeviceVersion,
                    MediaSourceFactory mediaSourceFactory,
                    CompositeTrackSelectorCreator trackSelectorCreator,
                    ExoPlayerCreator exoPlayerCreator,
                    RendererTypeRequesterCreator rendererTypeRequesterCreator) {
        this.bandwidthMeterCreator = bandwidthMeterCreator;
        this.androidDeviceVersion = androidDeviceVersion;
        this.mediaSourceFactory = mediaSourceFactory;
        this.trackSelectorCreator = trackSelectorCreator;
        this.exoPlayerCreator = exoPlayerCreator;
        this.rendererTypeRequesterCreator = rendererTypeRequesterCreator;
    }

    boolean isPlaying() {
        return exoPlayer != null && exoPlayer.getPlayWhenReady();
    }

    long playheadPositionInMillis() throws IllegalStateException {
        assertVideoLoaded();
        return exoPlayer.getCurrentPosition();
    }

    long mediaDurationInMillis() throws IllegalStateException {
        assertVideoLoaded();
        return exoPlayer.getDuration();
    }

    int bufferPercentage() throws IllegalStateException {
        assertVideoLoaded();
        return exoPlayer.getBufferedPercentage();
    }

    void play(long positionInMillis) throws IllegalStateException {
        seekTo(positionInMillis);
        play();
    }

    void play() throws IllegalStateException {
        assertVideoLoaded();
        exoPlayer.setPlayWhenReady(true);
    }

    void pause() throws IllegalStateException {
        assertVideoLoaded();
        exoPlayer.setPlayWhenReady(false);
    }

    void seekTo(long positionInMillis) throws IllegalStateException {
        assertVideoLoaded();
        exoPlayer.seekTo(positionInMillis);
    }

    void release() {
        if (exoPlayer != null) {
            exoPlayer.release();
            exoPlayer = null;
        }
    }

    void loadVideo(PlayerSurfaceHolder playerSurfaceHolder,
                   DrmSessionCreator drmSessionCreator,
                   Uri uri,
                   Options options,
                   ExoPlayerForwarder forwarder,
                   MediaCodecSelector mediaCodecSelector) {
        this.options = options;

        DefaultBandwidthMeter bandwidthMeter = bandwidthMeterCreator.create(options.maxInitialBitrate());

        compositeTrackSelector = trackSelectorCreator.create(options, bandwidthMeter);
        exoPlayer = exoPlayerCreator.create(
                drmSessionCreator,
                forwarder.drmSessionEventListener(),
                mediaCodecSelector,
                compositeTrackSelector.trackSelector()
        );
        rendererTypeRequester = rendererTypeRequesterCreator.createfrom(exoPlayer);
        exoPlayer.addListener(forwarder.exoPlayerEventListener());
        exoPlayer.addAnalyticsListener(forwarder.analyticsListener());
        exoPlayer.addVideoListener(forwarder.videoListener());

        setMovieAudioAttributes(exoPlayer);

        MediaSource mediaSource = mediaSourceFactory.create(
                options,
                uri,
                forwarder.mediaSourceEventListener(),
                bandwidthMeter
        );
        attachToSurface(playerSurfaceHolder);

        boolean hasInitialPosition = options.getInitialPositionInMillis().isPresent();
        if (hasInitialPosition) {
         
Download .txt
gitextract_28nzi1vk/

├── .github/
│   ├── contributing.md
│   ├── issue_template.md
│   ├── pull_request_template.md
│   └── workflows/
│       └── pull-request-builder.yml
├── .gitignore
├── .idea/
│   ├── codeStyleSettings.xml
│   ├── compiler.xml
│   ├── encodings.xml
│   └── vcs.xml
├── LICENSE
├── NOTICE
├── README.md
├── build.gradle
├── core/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── novoda/
│       │   │           └── noplayer/
│       │   │               ├── AndroidMediaPlayerCapabilities.java
│       │   │               ├── AspectRatioChangeCalculator.java
│       │   │               ├── ContentType.java
│       │   │               ├── DetailErrorType.java
│       │   │               ├── ExoPlayerCapabilities.java
│       │   │               ├── Listeners.java
│       │   │               ├── NoPlayer.java
│       │   │               ├── NoPlayerCreator.java
│       │   │               ├── NoPlayerError.java
│       │   │               ├── NoPlayerView.java
│       │   │               ├── Options.java
│       │   │               ├── OptionsBuilder.java
│       │   │               ├── PlayerBuilder.java
│       │   │               ├── PlayerCapabilities.java
│       │   │               ├── PlayerErrorType.java
│       │   │               ├── PlayerInformation.java
│       │   │               ├── PlayerState.java
│       │   │               ├── PlayerSurfaceHolder.java
│       │   │               ├── PlayerType.java
│       │   │               ├── PlayerView.java
│       │   │               ├── PlayerViewSurfaceHolder.java
│       │   │               ├── SubtitlePainter.java
│       │   │               ├── SubtitleView.java
│       │   │               ├── SurfaceRequester.java
│       │   │               ├── UnableToCreatePlayerException.java
│       │   │               ├── drm/
│       │   │               │   ├── DownloadedModularDrm.java
│       │   │               │   ├── DrmHandler.java
│       │   │               │   ├── DrmType.java
│       │   │               │   ├── ModularDrmKeyRequest.java
│       │   │               │   ├── ModularDrmProvisionRequest.java
│       │   │               │   └── StreamingModularDrm.java
│       │   │               ├── external/
│       │   │               │   └── exoplayer/
│       │   │               │       ├── text/
│       │   │               │       │   └── webvtt/
│       │   │               │       │       ├── CssParser.java
│       │   │               │       │       ├── WebvttCueParser.java
│       │   │               │       │       ├── WebvttDecoder.java
│       │   │               │       │       └── WebvttSubtitle.java
│       │   │               │       └── util/
│       │   │               │           └── ColorParser.java
│       │   │               ├── internal/
│       │   │               │   ├── Clock.java
│       │   │               │   ├── Heart.java
│       │   │               │   ├── SystemClock.java
│       │   │               │   ├── drm/
│       │   │               │   │   └── provision/
│       │   │               │   │       ├── HttpPostingProvisionExecutor.java
│       │   │               │   │       ├── HttpUrlConnectionPoster.java
│       │   │               │   │       ├── ProvisionExecutor.java
│       │   │               │   │       ├── ProvisionExecutorCreator.java
│       │   │               │   │       ├── ProvisioningCapabilities.java
│       │   │               │   │       └── UnableToProvisionException.java
│       │   │               │   ├── exoplayer/
│       │   │               │   │   ├── BandwidthMeterCreator.java
│       │   │               │   │   ├── CompositeTrackSelector.java
│       │   │               │   │   ├── CompositeTrackSelectorCreator.java
│       │   │               │   │   ├── ExoPlayerCreator.java
│       │   │               │   │   ├── ExoPlayerCueMapper.java
│       │   │               │   │   ├── ExoPlayerFacade.java
│       │   │               │   │   ├── ExoPlayerInformation.java
│       │   │               │   │   ├── ExoPlayerTwoImpl.java
│       │   │               │   │   ├── NoPlayerExoPlayerCreator.java
│       │   │               │   │   ├── RendererTypeRequester.java
│       │   │               │   │   ├── RendererTypeRequesterCreator.java
│       │   │               │   │   ├── SecurityDowngradingCodecSelector.java
│       │   │               │   │   ├── SimpleRenderersFactory.java
│       │   │               │   │   ├── TextRendererOutput.java
│       │   │               │   │   ├── drm/
│       │   │               │   │   │   ├── DownloadDrmSessionCreator.java
│       │   │               │   │   │   ├── DrmSessionCreator.java
│       │   │               │   │   │   ├── DrmSessionCreatorException.java
│       │   │               │   │   │   ├── DrmSessionCreatorFactory.java
│       │   │               │   │   │   ├── FrameworkDrmSession.java
│       │   │               │   │   │   ├── FrameworkMediaDrmCreator.java
│       │   │               │   │   │   ├── InvalidDrmSession.java
│       │   │               │   │   │   ├── LocalDrmSession.java
│       │   │               │   │   │   ├── LocalDrmSessionManager.java
│       │   │               │   │   │   ├── NoDrmSessionCreator.java
│       │   │               │   │   │   ├── ProvisioningModularDrmCallback.java
│       │   │               │   │   │   ├── SessionId.java
│       │   │               │   │   │   └── StreamingDrmSessionCreator.java
│       │   │               │   │   ├── error/
│       │   │               │   │   │   ├── ErrorFormatter.java
│       │   │               │   │   │   ├── ExoPlayerErrorMapper.java
│       │   │               │   │   │   ├── RendererErrorMapper.java
│       │   │               │   │   │   ├── SourceErrorMapper.java
│       │   │               │   │   │   └── UnexpectedErrorMapper.java
│       │   │               │   │   ├── forwarder/
│       │   │               │   │   │   ├── AnalyticsListenerForwarder.java
│       │   │               │   │   │   ├── BitrateForwarder.java
│       │   │               │   │   │   ├── BufferStateForwarder.java
│       │   │               │   │   │   ├── DrmSessionInfoForwarder.java
│       │   │               │   │   │   ├── EventInfoForwarder.java
│       │   │               │   │   │   ├── EventListener.java
│       │   │               │   │   │   ├── ExoPlayerDrmSessionEventListener.java
│       │   │               │   │   │   ├── ExoPlayerForwarder.java
│       │   │               │   │   │   ├── ExoPlayerVideoListener.java
│       │   │               │   │   │   ├── ForwarderInformation.java
│       │   │               │   │   │   ├── MediaSourceEventForwarder.java
│       │   │               │   │   │   ├── NoPlayerAnalyticsListener.java
│       │   │               │   │   │   ├── NoPlayerMediaSourceEventListener.java
│       │   │               │   │   │   ├── OnCompletionForwarder.java
│       │   │               │   │   │   ├── OnCompletionStateChangedForwarder.java
│       │   │               │   │   │   ├── OnPrepareForwarder.java
│       │   │               │   │   │   ├── PlayerOnErrorForwarder.java
│       │   │               │   │   │   └── VideoSizeChangedForwarder.java
│       │   │               │   │   └── mediasource/
│       │   │               │   │       ├── AudioTrackType.java
│       │   │               │   │       ├── ExoPlayerAudioTrackSelector.java
│       │   │               │   │       ├── ExoPlayerMappedTrackInfo.java
│       │   │               │   │       ├── ExoPlayerSubtitleTrackSelector.java
│       │   │               │   │       ├── ExoPlayerTrackSelector.java
│       │   │               │   │       ├── ExoPlayerVideoTrackSelector.java
│       │   │               │   │       ├── MediaSourceFactory.java
│       │   │               │   │       ├── RendererTrackIndexExtractor.java
│       │   │               │   │       └── TrackType.java
│       │   │               │   ├── listeners/
│       │   │               │   │   ├── BitrateChangedListeners.java
│       │   │               │   │   ├── BufferStateListeners.java
│       │   │               │   │   ├── CompletionListeners.java
│       │   │               │   │   ├── DroppedFramesListeners.java
│       │   │               │   │   ├── ErrorListeners.java
│       │   │               │   │   ├── HeartbeatCallbacks.java
│       │   │               │   │   ├── InfoListeners.java
│       │   │               │   │   ├── PlayerListenersHolder.java
│       │   │               │   │   ├── PreparedListeners.java
│       │   │               │   │   ├── StateChangedListeners.java
│       │   │               │   │   └── VideoSizeChangedListeners.java
│       │   │               │   ├── mediaplayer/
│       │   │               │   │   ├── AndroidMediaPlayerAudioTrackSelector.java
│       │   │               │   │   ├── AndroidMediaPlayerFacade.java
│       │   │               │   │   ├── AndroidMediaPlayerImpl.java
│       │   │               │   │   ├── AndroidMediaPlayerType.java
│       │   │               │   │   ├── BuggyVideoDriverPreventer.java
│       │   │               │   │   ├── CheckBufferHeartbeatCallback.java
│       │   │               │   │   ├── DelayedActionExecutor.java
│       │   │               │   │   ├── ErrorFactory.java
│       │   │               │   │   ├── ErrorFormatter.java
│       │   │               │   │   ├── MediaPlayerCreator.java
│       │   │               │   │   ├── MediaPlayerInformation.java
│       │   │               │   │   ├── MediaPlayerTypeReader.java
│       │   │               │   │   ├── NoPlayerMediaPlayerCreator.java
│       │   │               │   │   ├── NoPlayerTrackInfo.java
│       │   │               │   │   ├── NoPlayerTrackInfos.java
│       │   │               │   │   ├── OnPotentialBuggyDriverLayoutListener.java
│       │   │               │   │   ├── PlaybackStateChecker.java
│       │   │               │   │   ├── SystemProperties.java
│       │   │               │   │   ├── TrackInfosFactory.java
│       │   │               │   │   └── forwarder/
│       │   │               │   │       ├── BufferHeartbeatListener.java
│       │   │               │   │       ├── BufferInfoForwarder.java
│       │   │               │   │       ├── BufferOnPreparedListener.java
│       │   │               │   │       ├── CompletionForwarder.java
│       │   │               │   │       ├── CompletionInfoForwarder.java
│       │   │               │   │       ├── CompletionStateChangedForwarder.java
│       │   │               │   │       ├── ErrorForwarder.java
│       │   │               │   │       ├── ErrorInfoForwarder.java
│       │   │               │   │       ├── HeartBeatListener.java
│       │   │               │   │       ├── MediaPlayerCompletionListener.java
│       │   │               │   │       ├── MediaPlayerErrorListener.java
│       │   │               │   │       ├── MediaPlayerForwarder.java
│       │   │               │   │       ├── MediaPlayerPreparedListener.java
│       │   │               │   │       ├── OnPreparedForwarder.java
│       │   │               │   │       ├── OnPreparedInfoForwarder.java
│       │   │               │   │       ├── VideoSizeChangedForwarder.java
│       │   │               │   │       ├── VideoSizeChangedInfoForwarder.java
│       │   │               │   │       └── VideoSizeChangedListener.java
│       │   │               │   └── utils/
│       │   │               │       ├── AndroidDeviceVersion.java
│       │   │               │       ├── NoPlayerLog.java
│       │   │               │       └── Optional.java
│       │   │               ├── model/
│       │   │               │   ├── AudioTracks.java
│       │   │               │   ├── Bitrate.java
│       │   │               │   ├── Either.java
│       │   │               │   ├── KeySetId.java
│       │   │               │   ├── LoadTimeout.java
│       │   │               │   ├── NoPlayerCue.java
│       │   │               │   ├── PlayerAudioTrack.java
│       │   │               │   ├── PlayerSubtitleTrack.java
│       │   │               │   ├── PlayerVideoTrack.java
│       │   │               │   ├── TextCues.java
│       │   │               │   └── Timeout.java
│       │   │               └── text/
│       │   │                   └── NoPlayerSubtitleDecoderFactory.java
│       │   └── res/
│       │       └── layout/
│       │           └── noplayer_view.xml
│       └── test/
│           ├── java/
│           │   ├── com/
│           │   │   ├── google/
│           │   │   │   └── android/
│           │   │   │       └── exoplayer2/
│           │   │   │           ├── ExoPlaybackExceptionFactory.java
│           │   │   │           └── drm/
│           │   │   │               └── FrameworkMediaCryptoFixture.java
│           │   │   └── novoda/
│           │   │       └── noplayer/
│           │   │           ├── LoadTimeoutTest.java
│           │   │           ├── NoPlayerCreatorTest.java
│           │   │           ├── PlayerSurfaceHolderTest.java
│           │   │           ├── PlayerTypeTest.java
│           │   │           ├── internal/
│           │   │           │   ├── HeartTest.java
│           │   │           │   ├── drm/
│           │   │           │   │   └── provision/
│           │   │           │   │       ├── HttpPostingProvisionExecutorTest.java
│           │   │           │   │       └── ProvisioningCapabilitiesFixtures.java
│           │   │           │   ├── exoplayer/
│           │   │           │   │   ├── ExoPlayerFacadeTest.java
│           │   │           │   │   ├── ExoPlayerInformationTest.java
│           │   │           │   │   ├── ExoPlayerTwoImplTest.java
│           │   │           │   │   ├── NoPlayerExoPlayerCreatorTest.java
│           │   │           │   │   ├── PlayerSubtitleTrackFixture.java
│           │   │           │   │   ├── SecurityDowngradingCodecSelectorTest.java
│           │   │           │   │   ├── drm/
│           │   │           │   │   │   ├── DrmSessionCreatorFactoryTest.java
│           │   │           │   │   │   └── LocalDrmSessionManagerTest.java
│           │   │           │   │   ├── error/
│           │   │           │   │   │   └── ErrorFormatterTest.java
│           │   │           │   │   ├── forwarder/
│           │   │           │   │   │   └── ExoPlayerErrorMapperTest.java
│           │   │           │   │   └── mediasource/
│           │   │           │   │       ├── AudioFormatFixture.java
│           │   │           │   │       ├── AudioTrackTypeTest.java
│           │   │           │   │       ├── ExoPlayerAudioTrackSelectorTest.java
│           │   │           │   │       ├── ExoPlayerVideoTrackSelectorTest.java
│           │   │           │   │       ├── RendererTrackIndexExtractorTest.java
│           │   │           │   │       └── VideoFormatFixture.java
│           │   │           │   ├── listeners/
│           │   │           │   │   ├── BufferStateListenersTest.java
│           │   │           │   │   ├── CompletionListenersTest.java
│           │   │           │   │   └── StateChangedListenersTest.java
│           │   │           │   └── mediaplayer/
│           │   │           │       ├── AndroidMediaPlayerAudioTrackSelectorTest.java
│           │   │           │       ├── AndroidMediaPlayerFacadeTest.java
│           │   │           │       ├── AndroidMediaPlayerImplTest.java
│           │   │           │       ├── BuggyVideoDriverPreventerTest.java
│           │   │           │       ├── DelayedActionExecutorTest.java
│           │   │           │       ├── ErrorFactoryTest.java
│           │   │           │       ├── ErrorFormatterTest.java
│           │   │           │       ├── LoadTimeoutTest.java
│           │   │           │       ├── MediaPlayerInformationTest.java
│           │   │           │       ├── NoPlayerMediaPlayerCreatorTest.java
│           │   │           │       ├── OnPotentialBuggyDriverLayoutListenerTest.java
│           │   │           │       ├── PlaybackStateCheckerTest.java
│           │   │           │       └── PlayerCheckerTest.java
│           │   │           └── model/
│           │   │               ├── AudioTracksTest.java
│           │   │               ├── EitherTest.java
│           │   │               ├── PlayerAudioTrackFixture.java
│           │   │               └── PlayerVideoTrackFixture.java
│           │   └── utils/
│           │       └── ExceptionMatcher.java
│           └── resources/
│               └── mockito-extensions/
│                   └── org.mockito.plugins.MockMaker
├── demo/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── novoda/
│       │   │           └── demo/
│       │   │               ├── AndroidControllerView.java
│       │   │               ├── ControllerView.java
│       │   │               ├── DataPostingModularDrm.java
│       │   │               ├── DemoPresenter.java
│       │   │               ├── DialogCreator.java
│       │   │               ├── HttpClient.java
│       │   │               ├── MainActivity.java
│       │   │               ├── ProgressCalculator.java
│       │   │               └── TimeFormatter.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── progress.xml
│       │       │   └── thumb.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   ├── list_item.xml
│       │       │   └── merge_player_controls.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── colors.xml
│       │           ├── controls_styles.xml
│       │           ├── dimens.xml
│       │           ├── strings.xml
│       │           └── themes.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── novoda/
│                       └── demo/
│                           └── TimeFormatterTest.java
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── team-props/
    ├── static-analysis/
    │   ├── checkstyle-modules.xml
    │   ├── checkstyle-suppressions.xml
    │   ├── findbugs-excludes.xml
    │   ├── lint-config.xml
    │   └── pmd-rules.xml
    └── static-analysis.gradle
Download .txt
SYMBOL INDEX (1925 symbols across 223 files)

FILE: core/src/main/java/com/novoda/noplayer/AndroidMediaPlayerCapabilities.java
  class AndroidMediaPlayerCapabilities (line 8) | class AndroidMediaPlayerCapabilities implements PlayerCapabilities {
    method supports (line 12) | @Override

FILE: core/src/main/java/com/novoda/noplayer/AspectRatioChangeCalculator.java
  class AspectRatioChangeCalculator (line 3) | class AspectRatioChangeCalculator {
    method AspectRatioChangeCalculator (line 7) | AspectRatioChangeCalculator(Listener listener) {
    method onVideoSizeChanged (line 11) | void onVideoSizeChanged(int width, int height, float pixelWidthHeightR...
    method determineAspectRatio (line 16) | private float determineAspectRatio(int videoWidth, int videoHeight, fl...
    type Listener (line 23) | interface Listener {
      method onNewAspectRatio (line 25) | void onNewAspectRatio(float aspectRatio);

FILE: core/src/main/java/com/novoda/noplayer/ContentType.java
  type ContentType (line 3) | public enum ContentType {

FILE: core/src/main/java/com/novoda/noplayer/DetailErrorType.java
  type DetailErrorType (line 7) | public enum DetailErrorType {

FILE: core/src/main/java/com/novoda/noplayer/ExoPlayerCapabilities.java
  class ExoPlayerCapabilities (line 8) | class ExoPlayerCapabilities implements PlayerCapabilities {
    method supports (line 16) | @Override

FILE: core/src/main/java/com/novoda/noplayer/Listeners.java
  type Listeners (line 3) | public interface Listeners {
    method addErrorListener (line 10) | void addErrorListener(NoPlayer.ErrorListener errorListener);
    method removeErrorListener (line 17) | void removeErrorListener(NoPlayer.ErrorListener errorListener);
    method addPreparedListener (line 25) | void addPreparedListener(NoPlayer.PreparedListener preparedListener);
    method removePreparedListener (line 32) | void removePreparedListener(NoPlayer.PreparedListener preparedListener);
    method addBufferStateListener (line 39) | void addBufferStateListener(NoPlayer.BufferStateListener bufferStateLi...
    method removeBufferStateListener (line 46) | void removeBufferStateListener(NoPlayer.BufferStateListener bufferStat...
    method addCompletionListener (line 53) | void addCompletionListener(NoPlayer.CompletionListener completionListe...
    method removeCompletionListener (line 60) | void removeCompletionListener(NoPlayer.CompletionListener completionLi...
    method addStateChangedListener (line 68) | void addStateChangedListener(NoPlayer.StateChangedListener stateChange...
    method removeStateChangedListener (line 75) | void removeStateChangedListener(NoPlayer.StateChangedListener stateCha...
    method addInfoListener (line 83) | void addInfoListener(NoPlayer.InfoListener infoListener);
    method removeInfoListener (line 90) | void removeInfoListener(NoPlayer.InfoListener infoListener);
    method addBitrateChangedListener (line 97) | void addBitrateChangedListener(NoPlayer.BitrateChangedListener bitrate...
    method removeBitrateChangedListener (line 104) | void removeBitrateChangedListener(NoPlayer.BitrateChangedListener bitr...
    method addHeartbeatCallback (line 111) | void addHeartbeatCallback(NoPlayer.HeartbeatCallback heartbeatCallback);
    method removeHeartbeatCallback (line 118) | void removeHeartbeatCallback(NoPlayer.HeartbeatCallback heartbeatCallb...
    method addVideoSizeChangedListener (line 125) | void addVideoSizeChangedListener(NoPlayer.VideoSizeChangedListener vid...
    method removeVideoSizeChangedListener (line 132) | void removeVideoSizeChangedListener(NoPlayer.VideoSizeChangedListener ...
    method addDroppedVideoFrames (line 139) | void addDroppedVideoFrames(NoPlayer.DroppedVideoFramesListener dropped...
    method removeDroppedVideoFrames (line 146) | void removeDroppedVideoFrames(NoPlayer.DroppedVideoFramesListener drop...

FILE: core/src/main/java/com/novoda/noplayer/NoPlayer.java
  type NoPlayer (line 17) | public interface NoPlayer extends PlayerState {
    method getListeners (line 24) | Listeners getListeners();
    method play (line 32) | void play() throws IllegalStateException;
    method playAt (line 44) | @Deprecated
    method pause (line 53) | void pause() throws IllegalStateException;
    method seekTo (line 63) | void seekTo(long positionInMillis) throws IllegalStateException;
    method stop (line 68) | void stop();
    method release (line 74) | void release();
    method loadVideo (line 83) | void loadVideo(Uri uri, Options options) throws IllegalStateException;
    method loadVideoWithTimeout (line 94) | void loadVideoWithTimeout(Uri uri, Options options, Timeout timeout, L...
    method getPlayerInformation (line 101) | PlayerInformation getPlayerInformation();
    method attach (line 108) | void attach(PlayerView playerView);
    method detach (line 115) | void detach(PlayerView playerView);
    method getVideoTracks (line 124) | List<PlayerVideoTrack> getVideoTracks() throws IllegalStateException;
    method selectVideoTrack (line 133) | boolean selectVideoTrack(PlayerVideoTrack videoTrack) throws IllegalSt...
    method getSelectedVideoTrack (line 143) | Optional<PlayerVideoTrack> getSelectedVideoTrack() throws IllegalState...
    method clearVideoTrackSelection (line 151) | boolean clearVideoTrackSelection() throws IllegalStateException;
    method getAudioTracks (line 160) | AudioTracks getAudioTracks() throws IllegalStateException;
    method selectAudioTrack (line 169) | boolean selectAudioTrack(PlayerAudioTrack audioTrack) throws IllegalSt...
    method clearAudioTrackSelection (line 177) | boolean clearAudioTrackSelection() throws IllegalStateException;
    method getSubtitleTracks (line 186) | List<PlayerSubtitleTrack> getSubtitleTracks() throws IllegalStateExcep...
    method showSubtitleTrack (line 195) | boolean showSubtitleTrack(PlayerSubtitleTrack subtitleTrack) throws Il...
    method hideSubtitleTrack (line 203) | boolean hideSubtitleTrack() throws IllegalStateException;
    method setRepeating (line 211) | void setRepeating(boolean repeating) throws IllegalStateException;
    method setVolume (line 219) | void setVolume(@FloatRange(from = 0.0f, to = 1.0f) float volume) throw...
    method getVolume (line 227) | @FloatRange(from = 0.0f, to = 1.0f)
    method clearMaxVideoBitrate (line 233) | void clearMaxVideoBitrate();
    method setMaxVideoBitrate (line 240) | void setMaxVideoBitrate(int maxVideoBitrate);
    type PlayerError (line 242) | interface PlayerError {
      method type (line 244) | PlayerErrorType type();
      method detailType (line 246) | DetailErrorType detailType();
      method message (line 248) | String message();
    type ErrorListener (line 251) | interface ErrorListener {
      method onError (line 253) | void onError(PlayerError error);
    type PreparedListener (line 256) | interface PreparedListener {
      method onPrepared (line 258) | void onPrepared(PlayerState playerState);
    type BufferStateListener (line 261) | interface BufferStateListener {
      method onBufferStarted (line 263) | void onBufferStarted();
      method onBufferCompleted (line 265) | void onBufferCompleted();
    type CompletionListener (line 268) | interface CompletionListener {
      method onCompletion (line 270) | void onCompletion();
    type StateChangedListener (line 273) | interface StateChangedListener {
      method onVideoPlaying (line 275) | void onVideoPlaying();
      method onVideoPaused (line 277) | void onVideoPaused();
      method onVideoStopped (line 279) | void onVideoStopped();
    type BitrateChangedListener (line 282) | interface BitrateChangedListener {
      method onBitrateChanged (line 284) | void onBitrateChanged(Bitrate audioBitrate, Bitrate videoBitrate);
    type VideoSizeChangedListener (line 287) | interface VideoSizeChangedListener {
      method onVideoSizeChanged (line 289) | void onVideoSizeChanged(int width, int height, int unappliedRotation...
    type InfoListener (line 295) | interface InfoListener {
      method onNewInfo (line 306) | void onNewInfo(String callingMethod, Map<String, String> callingMeth...
    type LoadTimeoutCallback (line 309) | interface LoadTimeoutCallback {
      method onLoadTimeout (line 312) | @Override
      method onLoadTimeout (line 318) | void onLoadTimeout();
    type HeartbeatCallback (line 321) | interface HeartbeatCallback {
      method onBeat (line 323) | void onBeat(NoPlayer player);
    type DroppedVideoFramesListener (line 326) | interface DroppedVideoFramesListener {
      method onDroppedVideoFrames (line 328) | void onDroppedVideoFrames(int droppedFrames, long elapsedMsSinceLast...

FILE: core/src/main/java/com/novoda/noplayer/NoPlayerCreator.java
  class NoPlayerCreator (line 15) | class NoPlayerCreator {
    method NoPlayerCreator (line 23) | NoPlayerCreator(Context context,
    method create (line 35) | NoPlayer create(DrmType drmType, DrmHandler drmHandler, boolean downgr...
    method createPlayerForType (line 44) | private NoPlayer createPlayerForType(PlayerType playerType,

FILE: core/src/main/java/com/novoda/noplayer/NoPlayerError.java
  class NoPlayerError (line 3) | public class NoPlayerError implements NoPlayer.PlayerError {
    method NoPlayerError (line 9) | public NoPlayerError(PlayerErrorType playerErrorType, DetailErrorType ...
    method type (line 15) | @Override
    method detailType (line 20) | @Override
    method message (line 25) | @Override

FILE: core/src/main/java/com/novoda/noplayer/NoPlayerView.java
  class NoPlayerView (line 11) | public class NoPlayerView extends FrameLayout implements AspectRatioChan...
    method NoPlayerView (line 21) | public NoPlayerView(Context context, AttributeSet attrs) {
    method NoPlayerView (line 25) | public NoPlayerView(Context context, AttributeSet attrs, int defStyleA...
    method onFinishInflate (line 30) | @Override
    method onNewAspectRatio (line 41) | @Override
    method getContainerView (line 46) | @Override
    method getPlayerSurfaceHolder (line 51) | @Override
    method getVideoSizeChangedListener (line 56) | @Override
    method getStateChangedListener (line 61) | @Override
    method showSubtitles (line 66) | @Override
    method hideSubtitles (line 71) | @Override
    method setSubtitleCue (line 76) | @Override
    method onVideoSizeChanged (line 82) | @Override
    method onVideoPlaying (line 89) | @Override
    method onVideoPaused (line 94) | @Override
    method onVideoStopped (line 99) | @Override

FILE: core/src/main/java/com/novoda/noplayer/Options.java
  class Options (line 8) | public class Options {
    method toOptionsBuilder (line 21) | public OptionsBuilder toOptionsBuilder() {
    method Options (line 34) | Options(ContentType contentType,
    method contentType (line 46) | public ContentType contentType() {
    method minDurationBeforeQualityIncreaseInMillis (line 50) | public int minDurationBeforeQualityIncreaseInMillis() {
    method maxInitialBitrate (line 54) | public int maxInitialBitrate() {
    method maxVideoBitrate (line 58) | public int maxVideoBitrate() {
    method getInitialPositionInMillis (line 62) | public Optional<Long> getInitialPositionInMillis() {
    method equals (line 66) | @Override
    method hashCode (line 93) | @Override
    method toString (line 103) | @Override

FILE: core/src/main/java/com/novoda/noplayer/OptionsBuilder.java
  class OptionsBuilder (line 10) | public class OptionsBuilder {
    method withContentType (line 29) | public OptionsBuilder withContentType(ContentType contentType) {
    method withMinDurationBeforeQualityIncreaseInMillis (line 41) | public OptionsBuilder withMinDurationBeforeQualityIncreaseInMillis(int...
    method withMaxInitialBitrate (line 54) | public OptionsBuilder withMaxInitialBitrate(int maxInitialBitrate) {
    method withMaxVideoBitrate (line 67) | public OptionsBuilder withMaxVideoBitrate(int maxVideoBitrate) {
    method withInitialPositionInMillis (line 80) | public OptionsBuilder withInitialPositionInMillis(long initialPosition...
    method build (line 90) | public Options build() {

FILE: core/src/main/java/com/novoda/noplayer/PlayerBuilder.java
  class PlayerBuilder (line 24) | public class PlayerBuilder {
    method withWidevineClassicDrm (line 39) | public PlayerBuilder withWidevineClassicDrm() {
    method withWidevineModularStreamingDrm (line 50) | public PlayerBuilder withWidevineModularStreamingDrm(StreamingModularD...
    method withWidevineModularDownloadDrm (line 61) | public PlayerBuilder withWidevineModularDownloadDrm(DownloadedModularD...
    method withDrm (line 73) | public PlayerBuilder withDrm(DrmType drmType, DrmHandler drmHandler) {
    method withPriority (line 88) | public PlayerBuilder withPriority(PlayerType playerType, PlayerType......
    method withDowngradedSecureDecoder (line 102) | public PlayerBuilder withDowngradedSecureDecoder() {
    method withUserAgent (line 111) | public PlayerBuilder withUserAgent(String userAgent) {
    method allowCrossProtocolRedirects (line 120) | public PlayerBuilder allowCrossProtocolRedirects() {
    method build (line 133) | public NoPlayer build(Context context) throws UnableToCreatePlayerExce...

FILE: core/src/main/java/com/novoda/noplayer/PlayerCapabilities.java
  type PlayerCapabilities (line 5) | interface PlayerCapabilities {
    method supports (line 7) | boolean supports(DrmType drmType);

FILE: core/src/main/java/com/novoda/noplayer/PlayerErrorType.java
  type PlayerErrorType (line 3) | public enum PlayerErrorType {

FILE: core/src/main/java/com/novoda/noplayer/PlayerInformation.java
  type PlayerInformation (line 3) | public interface PlayerInformation {
    method getPlayerType (line 5) | PlayerType getPlayerType();
    method getVersion (line 7) | String getVersion();
    method getName (line 9) | String getName();

FILE: core/src/main/java/com/novoda/noplayer/PlayerState.java
  type PlayerState (line 3) | public interface PlayerState {
    method isPlaying (line 5) | boolean isPlaying();
    method videoWidth (line 7) | int videoWidth();
    method videoHeight (line 9) | int videoHeight();
    method playheadPositionInMillis (line 11) | long playheadPositionInMillis();
    method mediaDurationInMillis (line 13) | long mediaDurationInMillis();
    method bufferPercentage (line 15) | int bufferPercentage();

FILE: core/src/main/java/com/novoda/noplayer/PlayerSurfaceHolder.java
  class PlayerSurfaceHolder (line 8) | public class PlayerSurfaceHolder {
    method create (line 16) | public static PlayerSurfaceHolder create(SurfaceView surfaceView) {
    method create (line 22) | public static PlayerSurfaceHolder create(TextureView textureView) {
    method PlayerSurfaceHolder (line 28) | PlayerSurfaceHolder(@Nullable SurfaceView surfaceView, @Nullable Textu...
    method getSurfaceRequester (line 34) | public SurfaceRequester getSurfaceRequester() {
    method attach (line 38) | public void attach(Player.VideoComponent videoPlayer) {
    method containsSurfaceView (line 48) | private boolean containsSurfaceView() {
    method containsTextureView (line 52) | private boolean containsTextureView() {

FILE: core/src/main/java/com/novoda/noplayer/PlayerType.java
  type PlayerType (line 5) | public enum PlayerType {
    method PlayerType (line 11) | PlayerType(PlayerCapabilities playerCapabilities) {
    method supports (line 15) | boolean supports(DrmType drmType) {
    method from (line 19) | public static PlayerType from(String rawPlayerType) {
    class UnknownPlayerTypeException (line 28) | static class UnknownPlayerTypeException extends RuntimeException {
      method UnknownPlayerTypeException (line 30) | UnknownPlayerTypeException(String rawPlayerType) {

FILE: core/src/main/java/com/novoda/noplayer/PlayerView.java
  type PlayerView (line 6) | public interface PlayerView {
    method getContainerView (line 8) | View getContainerView();
    method getPlayerSurfaceHolder (line 10) | PlayerSurfaceHolder getPlayerSurfaceHolder();
    method getVideoSizeChangedListener (line 12) | NoPlayer.VideoSizeChangedListener getVideoSizeChangedListener();
    method getStateChangedListener (line 14) | NoPlayer.StateChangedListener getStateChangedListener();
    method showSubtitles (line 16) | void showSubtitles();
    method hideSubtitles (line 18) | void hideSubtitles();
    method setSubtitleCue (line 20) | void setSubtitleCue(TextCues textCues);

FILE: core/src/main/java/com/novoda/noplayer/PlayerViewSurfaceHolder.java
  class PlayerViewSurfaceHolder (line 13) | class PlayerViewSurfaceHolder implements SurfaceHolder.Callback, Texture...
    method surfaceCreated (line 19) | @Override
    method surfaceChanged (line 26) | @Override
    method surfaceDestroyed (line 31) | @Override
    method onSurfaceTextureAvailable (line 37) | @Override
    method notifyListeners (line 44) | private void notifyListeners(Either<Surface, SurfaceHolder> either) {
    method onSurfaceTextureSizeChanged (line 50) | @Override
    method onSurfaceTextureDestroyed (line 55) | @Override
    method onSurfaceTextureUpdated (line 63) | @Override
    method setSurfaceNotReady (line 68) | private void setSurfaceNotReady() {
    method requestSurface (line 72) | @Override
    method isSurfaceReady (line 81) | private boolean isSurfaceReady() {
    method removeCallback (line 85) | @Override

FILE: core/src/main/java/com/novoda/noplayer/SubtitlePainter.java
  class SubtitlePainter (line 45) | @SuppressWarnings({"PMD.GodClass", "PMD.CyclomaticComplexity", "PMD.StdC...
    method SubtitlePainter (line 104) | @SuppressWarnings("ResourceType")        // We're hacking `spacingMult...
    method draw (line 129) | @SuppressWarnings({"checkstyle:ParameterNumber", "PMD.ExcessiveParamet...
    method nothingHasChanged (line 199) | @SuppressWarnings({"checkstyle:ParameterNumber", "PMD.ExcessiveParamet...
    method setupTextLayout (line 236) | @SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.NPathComplexity" ...
    method setupBitmapLayout (line 342) | @SuppressWarnings("PMD.NPathComplexity")  // TODO break this method up
    method isCueDimensionSet (line 365) | private boolean isCueDimensionSet(float cueDimension) {
    method drawLayout (line 369) | private void drawLayout(Canvas canvas, boolean isTextCue) {
    method drawTextLayout (line 377) | @SuppressWarnings("PMD.NPathComplexity")  // TODO break this method up
    method drawBitmapLayout (line 437) | private void drawBitmapLayout(Canvas canvas) {
    method areCharSequencesEqual (line 446) | @SuppressWarnings("PMD.CompareObjectsWithEquals")   // We do, but we f...

FILE: core/src/main/java/com/novoda/noplayer/SubtitleView.java
  class SubtitleView (line 13) | public final class SubtitleView extends View {
    method SubtitleView (line 27) | public SubtitleView(Context context, AttributeSet attrs) {
    method setCues (line 32) | public void setCues(TextCues textCues) {
    method dispatchDraw (line 47) | @Override

FILE: core/src/main/java/com/novoda/noplayer/SurfaceRequester.java
  type SurfaceRequester (line 7) | public interface SurfaceRequester {
    method requestSurface (line 9) | void requestSurface(Callback callback);
    method removeCallback (line 11) | void removeCallback(Callback callback);
    type Callback (line 13) | interface Callback {
      method onSurfaceReady (line 15) | void onSurfaceReady(Either<Surface, SurfaceHolder> surface);

FILE: core/src/main/java/com/novoda/noplayer/UnableToCreatePlayerException.java
  class UnableToCreatePlayerException (line 6) | public class UnableToCreatePlayerException extends RuntimeException {
    method unhandledDrmType (line 8) | static UnableToCreatePlayerException unhandledDrmType(DrmType drmType) {
    method unhandledPlayerType (line 12) | static UnableToCreatePlayerException unhandledPlayerType(PlayerType pl...
    method deviceDoesNotMeetTargetApiException (line 16) | public static UnableToCreatePlayerException deviceDoesNotMeetTargetApi...
    method UnableToCreatePlayerException (line 29) | UnableToCreatePlayerException(Throwable cause) {
    method UnableToCreatePlayerException (line 33) | private UnableToCreatePlayerException(String reason) {

FILE: core/src/main/java/com/novoda/noplayer/drm/DownloadedModularDrm.java
  type DownloadedModularDrm (line 5) | public interface DownloadedModularDrm extends DrmHandler {
    method getKeySetId (line 7) | KeySetId getKeySetId();

FILE: core/src/main/java/com/novoda/noplayer/drm/DrmHandler.java
  type DrmHandler (line 3) | @SuppressWarnings({

FILE: core/src/main/java/com/novoda/noplayer/drm/DrmType.java
  type DrmType (line 3) | public enum DrmType {

FILE: core/src/main/java/com/novoda/noplayer/drm/ModularDrmKeyRequest.java
  class ModularDrmKeyRequest (line 5) | public class ModularDrmKeyRequest {
    method ModularDrmKeyRequest (line 10) | public ModularDrmKeyRequest(String url, byte[] data) {
    method url (line 15) | public String url() {
    method data (line 19) | public byte[] data() {
    method equals (line 23) | @Override
    method hashCode (line 40) | @Override
    method toString (line 47) | @Override

FILE: core/src/main/java/com/novoda/noplayer/drm/ModularDrmProvisionRequest.java
  class ModularDrmProvisionRequest (line 5) | public class ModularDrmProvisionRequest {
    method ModularDrmProvisionRequest (line 10) | public ModularDrmProvisionRequest(String url, byte[] data) {
    method url (line 15) | public String url() {
    method data (line 19) | public byte[] data() {
    method equals (line 23) | @Override
    method hashCode (line 40) | @Override
    method toString (line 47) | @Override

FILE: core/src/main/java/com/novoda/noplayer/drm/StreamingModularDrm.java
  type StreamingModularDrm (line 3) | public interface StreamingModularDrm extends DrmHandler {
    method executeKeyRequest (line 5) | byte[] executeKeyRequest(ModularDrmKeyRequest request) throws DrmReque...
    class DrmRequestException (line 7) | final class DrmRequestException extends Exception {
      method from (line 9) | public static DrmRequestException from(Exception e) {
      method invalidHttpCode (line 13) | public static DrmRequestException invalidHttpCode(int code, String b...
      method DrmRequestException (line 17) | private DrmRequestException(String detailMessage) {
      method DrmRequestException (line 21) | private DrmRequestException(String message, Throwable cause) {

FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/CssParser.java
  class CssParser (line 32) | final class CssParser {
    method CssParser (line 51) | public CssParser() {
    method parseBlock (line 64) | public WebvttCssStyle parseBlock(ParsableByteArray input) {
    method parseSelector (line 98) | private static String parseSelector(ParsableByteArray input, StringBui...
    method readCueTarget (line 130) | private static String readCueTarget(ParsableByteArray input) {
    method parseStyleDeclaration (line 142) | private static void parseStyleDeclaration(ParsableByteArray input, Web...
    method skipWhitespaceAndComments (line 193) | static void skipWhitespaceAndComments(ParsableByteArray input) {
    method parseNextToken (line 201) | static String parseNextToken(ParsableByteArray input, StringBuilder st...
    method maybeSkipWhitespace (line 214) | private static boolean maybeSkipWhitespace(ParsableByteArray input) {
    method skipStyleBlock (line 229) | static void skipStyleBlock(ParsableByteArray input) {
    method peekCharAtPosition (line 238) | private static char peekCharAtPosition(ParsableByteArray input, int po...
    method parsePropertyValue (line 242) | private static String parsePropertyValue(ParsableByteArray input, Stri...
    method maybeSkipComment (line 265) | private static boolean maybeSkipComment(ParsableByteArray input) {
    method parseIdentifier (line 285) | private static String parseIdentifier(ParsableByteArray input, StringB...
    method applySelectorToStyle (line 308) | private void applySelectorToStyle(WebvttCssStyle style, String selecto...

FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttCueParser.java
  class WebvttCueParser (line 54) | public final class WebvttCueParser {
    method WebvttCueParser (line 87) | public WebvttCueParser() {
    method parseCue (line 99) | public boolean parseCue(ParsableByteArray webvttData, WebvttCue.Builde...
    method parseCueSettingsList (line 132) | static void parseCueSettingsList(String cueSettingsList,
    method parseCueText (line 166) | static void parseCueText(String id, String markup, WebvttCue.Builder b...
    method parseCue (line 238) | private static boolean parseCue(String id, Matcher cueHeaderMatcher, P...
    method parseLineAttribute (line 266) | private static void parseLineAttribute(String s, WebvttCue.Builder bui...
    method parsePositionAttribute (line 288) | private static void parsePositionAttribute(String s, WebvttCue.Builder...
    method parsePositionAnchor (line 300) | private static int parsePositionAnchor(String s) {
    method parseTextAlignment (line 315) | private static Alignment parseTextAlignment(String s) {
    method findEndOfTag (line 339) | private static int findEndOfTag(String markup, int startPos) {
    method applyEntity (line 344) | private static void applyEntity(String entity, SpannableStringBuilder ...
    method isSupportedTag (line 364) | private static boolean isSupportedTag(String tagName) {
    method applySpansForTag (line 378) | private static void applySpansForTag(String cueId, StartTag startTag, ...
    method applySupportedClasses (line 414) | private static void applySupportedClasses(SpannableStringBuilder text,...
    method applyStyleToText (line 424) | private static void applyStyleToText(SpannableStringBuilder spannedTex...
    method getTagName (line 488) | private static String getTagName(String tagExpression) {
    method getApplicableStyles (line 496) | private static void getApplicableStyles(List<WebvttCssStyle> declaredS...
    class StyleMatch (line 509) | private static final class StyleMatch implements Comparable<StyleMatch> {
      method StyleMatch (line 514) | public StyleMatch(int score, WebvttCssStyle style) {
      method compareTo (line 519) | @Override
    class StartTag (line 526) | private static final class StartTag {
      method StartTag (line 535) | private StartTag(String name, int position, String voice, String[] c...
      method buildStartTag (line 542) | public static StartTag buildStartTag(String fullTagExpression, int p...
      method buildWholeCueVirtualTag (line 566) | public static StartTag buildWholeCueVirtualTag() {

FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttDecoder.java
  class WebvttDecoder (line 36) | public final class WebvttDecoder extends SimpleSubtitleDecoder {
    method WebvttDecoder (line 53) | public WebvttDecoder() {
    method decode (line 62) | @Override
    method getNextEvent (line 109) | private static int getNextEvent(ParsableByteArray parsableWebvttData) {
    method skipComment (line 129) | private static void skipComment(ParsableByteArray parsableWebvttData) {

FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttSubtitle.java
  class WebvttSubtitle (line 35) | final class WebvttSubtitle implements Subtitle {
    method WebvttSubtitle (line 45) | public WebvttSubtitle(List<WebvttCue> cues) {
    method getNextEventTimeIndex (line 59) | @Override
    method getEventTimeCount (line 65) | @Override
    method getEventTime (line 70) | @Override
    method getCues (line 77) | @Override

FILE: core/src/main/java/com/novoda/noplayer/external/exoplayer/util/ColorParser.java
  class ColorParser (line 34) | public final class ColorParser {
    method isNamedColor (line 50) | public static boolean isNamedColor(String expression) {
    method parseTtmlColor (line 60) | public static int parseTtmlColor(String colorExpression) {
    method parseCssColor (line 70) | public static int parseCssColor(String colorExpression) {
    method parseColorInternal (line 74) | private static int parseColorInternal(String colorExpression, boolean ...
    method argb (line 121) | private static int argb(int alpha, int red, int green, int blue) {
    method rgb (line 125) | private static int rgb(int red, int green, int blue) {

FILE: core/src/main/java/com/novoda/noplayer/internal/Clock.java
  type Clock (line 5) | public interface Clock extends Serializable {
    method getCurrentTime (line 7) | long getCurrentTime();

FILE: core/src/main/java/com/novoda/noplayer/internal/Heart.java
  class Heart (line 7) | @SuppressWarnings("checkstyle:FinalClass")  // We cannot make it final a...
    method newInstance (line 19) | public static Heart newInstance(Handler handler) {
    method Heart (line 23) | private Heart(Handler handler, long heartbeatFrequencyInMillis) {
    method bind (line 28) | public void bind(Heartbeat onHeartbeat) {
    method startBeatingHeart (line 32) | public void startBeatingHeart() {
    method run (line 42) | @Override
    method scheduleNextBeat (line 49) | private void scheduleNextBeat() {
    method stopBeatingHeart (line 53) | public void stopBeatingHeart() {
    method forceBeat (line 58) | public void forceBeat() {
    method isBeating (line 65) | public boolean isBeating() {
    class Heartbeat (line 69) | public static class Heartbeat implements Runnable {
      method Heartbeat (line 74) | public Heartbeat(NoPlayer.HeartbeatCallback callback, NoPlayer playe...
      method run (line 79) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/SystemClock.java
  class SystemClock (line 3) | public class SystemClock implements Clock {
    method getCurrentTime (line 5) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/HttpPostingProvisionExecutor.java
  class HttpPostingProvisionExecutor (line 8) | class HttpPostingProvisionExecutor implements ProvisionExecutor {
    method HttpPostingProvisionExecutor (line 15) | HttpPostingProvisionExecutor(HttpUrlConnectionPoster httpPoster, Provi...
    method execute (line 20) | @Override
    method isIncapableOfProvisioning (line 29) | private boolean isIncapableOfProvisioning() {
    method buildProvisioningUrl (line 33) | private String buildProvisioningUrl(ModularDrmProvisionRequest request) {

FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/HttpUrlConnectionPoster.java
  class HttpUrlConnectionPoster (line 9) | class HttpUrlConnectionPoster {
    method post (line 14) | byte[] post(String url) throws IOException {
    method byteArrayFrom (line 28) | private byte[] byteArrayFrom(HttpURLConnection urlConnection) throws I...
    method byteArrayFrom (line 37) | private byte[] byteArrayFrom(InputStream inputStream) throws IOExcepti...

FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisionExecutor.java
  type ProvisionExecutor (line 7) | public interface ProvisionExecutor {
    method execute (line 9) | byte[] execute(ModularDrmProvisionRequest request) throws IOException,...

FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisionExecutorCreator.java
  class ProvisionExecutorCreator (line 3) | public class ProvisionExecutorCreator {
    method create (line 5) | public ProvisionExecutor create() {

FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisioningCapabilities.java
  class ProvisioningCapabilities (line 6) | class ProvisioningCapabilities {
    method newInstance (line 10) | static ProvisioningCapabilities newInstance() {
    method ProvisioningCapabilities (line 14) | @VisibleForTesting
    method canProvision (line 19) | boolean canProvision() {

FILE: core/src/main/java/com/novoda/noplayer/internal/drm/provision/UnableToProvisionException.java
  class UnableToProvisionException (line 5) | public class UnableToProvisionException extends Exception {
    method UnableToProvisionException (line 7) | UnableToProvisionException() {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/BandwidthMeterCreator.java
  class BandwidthMeterCreator (line 7) | class BandwidthMeterCreator {
    method BandwidthMeterCreator (line 10) | BandwidthMeterCreator(Context context) {
    method create (line 14) | DefaultBandwidthMeter create(long maxInitialBitrate) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/CompositeTrackSelector.java
  class CompositeTrackSelector (line 18) | class CompositeTrackSelector {
    method CompositeTrackSelector (line 25) | CompositeTrackSelector(DefaultTrackSelector defaultTrackSelector,
    method trackSelector (line 35) | TrackSelector trackSelector() {
    method selectAudioTrack (line 39) | boolean selectAudioTrack(PlayerAudioTrack audioTrack, RendererTypeRequ...
    method getAudioTracks (line 43) | AudioTracks getAudioTracks(RendererTypeRequester rendererTypeRequester) {
    method clearAudioTrack (line 47) | boolean clearAudioTrack(RendererTypeRequester rendererTypeRequester) {
    method selectVideoTrack (line 51) | boolean selectVideoTrack(PlayerVideoTrack videoTrack, RendererTypeRequ...
    method getVideoTracks (line 55) | List<PlayerVideoTrack> getVideoTracks(RendererTypeRequester rendererTy...
    method getSelectedVideoTrack (line 59) | Optional<PlayerVideoTrack> getSelectedVideoTrack(SimpleExoPlayer exoPl...
    method clearVideoTrack (line 65) | boolean clearVideoTrack(RendererTypeRequester rendererTypeRequester) {
    method selectTextTrack (line 69) | boolean selectTextTrack(PlayerSubtitleTrack subtitleTrack, RendererTyp...
    method getSubtitleTracks (line 73) | List<PlayerSubtitleTrack> getSubtitleTracks(RendererTypeRequester rend...
    method clearSubtitleTrack (line 77) | boolean clearSubtitleTrack(RendererTypeRequester rendererTypeRequester) {
    method clearMaxVideoBitrate (line 81) | void clearMaxVideoBitrate() {
    method setMaxVideoBitrate (line 85) | void setMaxVideoBitrate(int maxVideoBitrate) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/CompositeTrackSelectorCreator.java
  class CompositeTrackSelectorCreator (line 14) | class CompositeTrackSelectorCreator {
    method create (line 16) | CompositeTrackSelector create(Options options, DefaultBandwidthMeter b...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerCreator.java
  class ExoPlayerCreator (line 21) | class ExoPlayerCreator {
    method ExoPlayerCreator (line 27) | ExoPlayerCreator(Context context) {
    method create (line 31) | @NonNull

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerCueMapper.java
  class ExoPlayerCueMapper (line 11) | final class ExoPlayerCueMapper {
    method ExoPlayerCueMapper (line 13) | private ExoPlayerCueMapper() {
    method map (line 17) | static TextCues map(List<Cue> cues) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerFacade.java
  class ExoPlayerFacade (line 27) | class ExoPlayerFacade {
    method ExoPlayerFacade (line 47) | ExoPlayerFacade(BandwidthMeterCreator bandwidthMeterCreator,
    method isPlaying (line 61) | boolean isPlaying() {
    method playheadPositionInMillis (line 65) | long playheadPositionInMillis() throws IllegalStateException {
    method mediaDurationInMillis (line 70) | long mediaDurationInMillis() throws IllegalStateException {
    method bufferPercentage (line 75) | int bufferPercentage() throws IllegalStateException {
    method play (line 80) | void play(long positionInMillis) throws IllegalStateException {
    method play (line 85) | void play() throws IllegalStateException {
    method pause (line 90) | void pause() throws IllegalStateException {
    method seekTo (line 95) | void seekTo(long positionInMillis) throws IllegalStateException {
    method release (line 100) | void release() {
    method loadVideo (line 107) | void loadVideo(PlayerSurfaceHolder playerSurfaceHolder,
    method setMovieAudioAttributes (line 148) | private void setMovieAudioAttributes(SimpleExoPlayer exoPlayer) {
    method attachToSurface (line 157) | private void attachToSurface(PlayerSurfaceHolder playerSurfaceHolder) {
    method getAudioTracks (line 161) | AudioTracks getAudioTracks() throws IllegalStateException {
    method selectAudioTrack (line 166) | boolean selectAudioTrack(PlayerAudioTrack audioTrack) throws IllegalSt...
    method clearAudioTrackSelection (line 171) | boolean clearAudioTrackSelection() {
    method selectVideoTrack (line 176) | boolean selectVideoTrack(PlayerVideoTrack playerVideoTrack) {
    method getSelectedVideoTrack (line 181) | Optional<PlayerVideoTrack> getSelectedVideoTrack() {
    method getVideoTracks (line 186) | List<PlayerVideoTrack> getVideoTracks() {
    method clearVideoTrackSelection (line 191) | boolean clearVideoTrackSelection() {
    method setSubtitleRendererOutput (line 196) | void setSubtitleRendererOutput(TextRendererOutput textRendererOutput) ...
    method removeSubtitleRendererOutput (line 201) | void removeSubtitleRendererOutput(TextRendererOutput textRendererOutpu...
    method selectSubtitleTrack (line 206) | boolean selectSubtitleTrack(PlayerSubtitleTrack subtitleTrack) throws ...
    method getSubtitleTracks (line 211) | List<PlayerSubtitleTrack> getSubtitleTracks() throws IllegalStateExcep...
    method hasPlayedContent (line 216) | boolean hasPlayedContent() {
    method clearSubtitleTrackSelection (line 220) | boolean clearSubtitleTrackSelection() throws IllegalStateException {
    method setRepeating (line 225) | void setRepeating(boolean repeating) {
    method setVolume (line 230) | void setVolume(float volume) {
    method getVolume (line 235) | float getVolume() {
    method clearMaxVideoBitrate (line 240) | void clearMaxVideoBitrate() {
    method setMaxVideoBitrate (line 245) | void setMaxVideoBitrate(int maxVideoBitrate) {
    method assertVideoLoaded (line 250) | private void assertVideoLoaded() {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerInformation.java
  class ExoPlayerInformation (line 7) | class ExoPlayerInformation implements PlayerInformation {
    method getPlayerType (line 8) | @Override
    method getVersion (line 13) | @Override
    method getName (line 18) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerTwoImpl.java
  class ExoPlayerTwoImpl (line 29) | @SuppressWarnings("PMD.GodClass")
    method ExoPlayerTwoImpl (line 47) | ExoPlayerTwoImpl(ExoPlayerFacade exoPlayer,
    method initialise (line 63) | void initialise() {
    method isPlaying (line 94) | @Override
    method videoWidth (line 99) | @Override
    method videoHeight (line 104) | @Override
    method playheadPositionInMillis (line 109) | @Override
    method mediaDurationInMillis (line 114) | @Override
    method bufferPercentage (line 119) | @Override
    method setRepeating (line 124) | @Override
    method setVolume (line 129) | @Override
    method getVolume (line 134) | @Override
    method clearMaxVideoBitrate (line 139) | @Override
    method setMaxVideoBitrate (line 144) | @Override
    method getListeners (line 149) | @Override
    method play (line 154) | @Override
    method playAt (line 161) | @Override
    method pause (line 167) | @Override
    method seekTo (line 177) | @Override
    method stop (line 182) | @Override
    method release (line 188) | @Override
    method reset (line 194) | private void reset() {
    method destroySurfaceByHidingVideoContainer (line 202) | private void destroySurfaceByHidingVideoContainer() {
    method loadVideo (line 208) | @Override
    method assertPlayerViewIsAttached (line 218) | private void assertPlayerViewIsAttached() {
    method createSurfaceByShowingVideoContainer (line 224) | private void createSurfaceByShowingVideoContainer() {
    method loadVideoWithTimeout (line 228) | @Override
    method getPlayerInformation (line 234) | @Override
    method attach (line 239) | @Override
    method detach (line 246) | @Override
    method selectAudioTrack (line 254) | @Override
    method clearAudioTrackSelection (line 259) | @Override
    method showSubtitleTrack (line 264) | @Override
    method setSubtitleRendererOutput (line 271) | private void setSubtitleRendererOutput() throws IllegalStateException {
    method hideSubtitleTrack (line 277) | @Override
    method removeSubtitleRenderer (line 284) | private void removeSubtitleRenderer() {
    method getAudioTracks (line 290) | @Override
    method selectVideoTrack (line 295) | @Override
    method getSelectedVideoTrack (line 300) | @Override
    method clearVideoTrackSelection (line 305) | @Override
    method getVideoTracks (line 310) | @Override
    method getSubtitleTracks (line 315) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreator.java
  class NoPlayerExoPlayerCreator (line 19) | public class NoPlayerExoPlayerCreator {
    method newInstance (line 23) | public static NoPlayerExoPlayerCreator newInstance(String userAgent, H...
    method newInstance (line 28) | public static NoPlayerExoPlayerCreator newInstance(String userAgent, H...
    method NoPlayerExoPlayerCreator (line 33) | NoPlayerExoPlayerCreator(InternalCreator internalCreator) {
    method createExoPlayer (line 37) | public NoPlayer createExoPlayer(Context context,
    class InternalCreator (line 46) | static class InternalCreator {
      method InternalCreator (line 52) | InternalCreator(String userAgent, Handler handler, Optional<DataSour...
      method create (line 58) | ExoPlayerTwoImpl create(Context context,

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/RendererTypeRequester.java
  type RendererTypeRequester (line 3) | public interface RendererTypeRequester {
    method getRendererTypeFor (line 5) | int getRendererTypeFor(int index);

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/RendererTypeRequesterCreator.java
  class RendererTypeRequesterCreator (line 5) | class RendererTypeRequesterCreator {
    method createfrom (line 7) | RendererTypeRequester createfrom(final SimpleExoPlayer exoPlayer) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/SecurityDowngradingCodecSelector.java
  class SecurityDowngradingCodecSelector (line 9) | class SecurityDowngradingCodecSelector implements MediaCodecSelector {
    method newInstance (line 15) | public static SecurityDowngradingCodecSelector newInstance() {
    method SecurityDowngradingCodecSelector (line 20) | SecurityDowngradingCodecSelector(InternalMediaCodecUtil internalMediaC...
    method getDecoderInfos (line 24) | @Override
    method getPassthroughDecoderInfo (line 29) | @Override
    class InternalMediaCodecUtil (line 34) | static class InternalMediaCodecUtil {
      method getDecoderInfos (line 36) | List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean requir...
      method getPassthroughDecoderInfo (line 40) | MediaCodecInfo getPassthroughDecoderInfo() throws MediaCodecUtil.Dec...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/SimpleRenderersFactory.java
  class SimpleRenderersFactory (line 53) | class SimpleRenderersFactory implements RenderersFactory {
    method SimpleRenderersFactory (line 111) | SimpleRenderersFactory(Context context,
    method createRenderers (line 123) | @Override
    method buildVideoRenderers (line 155) | @SuppressWarnings({"PMD.AvoidCatchingGenericException"})   // Using re...
    method buildAudioRenderers (line 210) | @SuppressWarnings({"PMD.AvoidCatchingGenericException"})   // Using re...
    method buildTextRenderers (line 284) | private void buildTextRenderers(TextOutput output, Looper outputLooper...
    method buildMetadataRenderers (line 296) | private void buildMetadataRenderers(MetadataOutput output, Looper outp...
    method buildMiscellaneousRenderers (line 303) | private void buildMiscellaneousRenderers() {
    method buildAudioProcessors (line 310) | private AudioProcessor[] buildAudioProcessors() {
    class RendererInstantiationException (line 314) | public static class RendererInstantiationException extends RuntimeExce...
      method RendererInstantiationException (line 316) | RendererInstantiationException(String rendererName, Throwable cause) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/TextRendererOutput.java
  class TextRendererOutput (line 10) | class TextRendererOutput {
    method TextRendererOutput (line 14) | TextRendererOutput(PlayerView playerView) {
    method output (line 18) | TextRenderer.Output output() {
    method equals (line 28) | @Override
    method hashCode (line 42) | @Override
    method toString (line 47) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DownloadDrmSessionCreator.java
  class DownloadDrmSessionCreator (line 10) | class DownloadDrmSessionCreator implements DrmSessionCreator {
    method DownloadDrmSessionCreator (line 16) | DownloadDrmSessionCreator(DownloadedModularDrm downloadedModularDrm, F...
    method create (line 22) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreator.java
  type DrmSessionCreator (line 11) | public interface DrmSessionCreator {
    method create (line 15) | @Nullable

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreatorException.java
  class DrmSessionCreatorException (line 5) | public final class DrmSessionCreatorException extends Exception {
    method noDrmHandlerFor (line 7) | static DrmSessionCreatorException noDrmHandlerFor(DrmType drmType) {
    method DrmSessionCreatorException (line 11) | private DrmSessionCreatorException(String message) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreatorFactory.java
  class DrmSessionCreatorFactory (line 15) | public class DrmSessionCreatorFactory {
    method DrmSessionCreatorFactory (line 21) | public DrmSessionCreatorFactory(AndroidDeviceVersion androidDeviceVers...
    method createFor (line 27) | public DrmSessionCreator createFor(DrmType drmType, DrmHandler drmHand...
    method assertThatApiLevelIsJellyBeanEighteenOrAbove (line 44) | private void assertThatApiLevelIsJellyBeanEighteenOrAbove(DrmType drmT...
    method createModularStream (line 55) | private DrmSessionCreator createModularStream(StreamingModularDrm drmH...
    method createModularDownload (line 65) | private DownloadDrmSessionCreator createModularDownload(DownloadedModu...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/FrameworkDrmSession.java
  type FrameworkDrmSession (line 6) | interface FrameworkDrmSession extends DrmSession<FrameworkMediaCrypto> {
    method getSessionId (line 8) | SessionId getSessionId();

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/FrameworkMediaDrmCreator.java
  class FrameworkMediaDrmCreator (line 8) | class FrameworkMediaDrmCreator {
    method create (line 10) | @SuppressWarnings("PMD.PreserveStackTrace")  // We just unwrap the exc...
    class FrameworkMediaDrmException (line 19) | private static class FrameworkMediaDrmException extends RuntimeExcepti...
      method FrameworkMediaDrmException (line 21) | FrameworkMediaDrmException(String message, Throwable cause) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/InvalidDrmSession.java
  class InvalidDrmSession (line 10) | class InvalidDrmSession implements FrameworkDrmSession {
    method InvalidDrmSession (line 16) | InvalidDrmSession(DrmSessionException drmSessionException) {
    method getState (line 20) | @Override
    method getMediaCrypto (line 25) | @Override
    method getError (line 30) | @Override
    method queryKeyStatus (line 35) | @Override
    method getOfflineLicenseKeySetId (line 40) | @SuppressWarnings("PMD.MethodReturnsInternalArray") // We return a con...
    method getSessionId (line 47) | @Override
    method equals (line 52) | @Override
    method hashCode (line 66) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/LocalDrmSession.java
  class LocalDrmSession (line 12) | class LocalDrmSession implements FrameworkDrmSession {
    method LocalDrmSession (line 20) | LocalDrmSession(FrameworkMediaCrypto mediaCrypto, KeySetId keySetIdToR...
    method getState (line 26) | @Override
    method getMediaCrypto (line 31) | @Override
    method getError (line 36) | @Nullable
    method queryKeyStatus (line 42) | @Override
    method getOfflineLicenseKeySetId (line 47) | @Override
    method getSessionId (line 52) | @Override
    method equals (line 57) | @Override
    method hashCode (line 77) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/LocalDrmSessionManager.java
  class LocalDrmSessionManager (line 18) | class LocalDrmSessionManager implements DrmSessionManager<FrameworkMedia...
    method LocalDrmSessionManager (line 26) | LocalDrmSessionManager(KeySetId keySetIdToRestore,
    method canAcquireSession (line 38) | @Override
    method acquireSession (line 44) | @SuppressWarnings("PMD.AvoidCatchingGenericException") // We are force...
    method notifyErrorListener (line 64) | private void notifyErrorListener(DrmSession<FrameworkMediaCrypto> drmS...
    method releaseSession (line 74) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/NoDrmSessionCreator.java
  class NoDrmSessionCreator (line 9) | class NoDrmSessionCreator implements DrmSessionCreator {
    method create (line 13) | @Nullable

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/ProvisioningModularDrmCallback.java
  class ProvisioningModularDrmCallback (line 12) | class ProvisioningModularDrmCallback implements MediaDrmCallback {
    method ProvisioningModularDrmCallback (line 17) | ProvisioningModularDrmCallback(StreamingModularDrm streamingModularDrm...
    method executeProvisionRequest (line 22) | @Override
    method executeKeyRequest (line 27) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/SessionId.java
  class SessionId (line 5) | final class SessionId {
    method absent (line 9) | static SessionId absent() {
    method of (line 13) | static SessionId of(byte[] sessionId) {
    method SessionId (line 17) | @SuppressWarnings("PMD.ArrayIsStoredDirectly")  // This can only come ...
    method asBytes (line 22) | byte[] asBytes() {
    method equals (line 26) | @Override
    method hashCode (line 40) | @Override
    method toString (line 45) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/StreamingDrmSessionCreator.java
  class StreamingDrmSessionCreator (line 14) | class StreamingDrmSessionCreator implements DrmSessionCreator {
    method StreamingDrmSessionCreator (line 23) | StreamingDrmSessionCreator(MediaDrmCallback mediaDrmCallback, Framewor...
    method create (line 29) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/ErrorFormatter.java
  class ErrorFormatter (line 7) | final class ErrorFormatter {
    method ErrorFormatter (line 9) | private ErrorFormatter() {
    method formatMessage (line 13) | static String formatMessage(Throwable throwable) {
    method formatCodecException (line 17) | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/ExoPlayerErrorMapper.java
  class ExoPlayerErrorMapper (line 9) | public final class ExoPlayerErrorMapper {
    method ExoPlayerErrorMapper (line 11) | private ExoPlayerErrorMapper() {
    method errorFor (line 15) | public static NoPlayer.PlayerError errorFor(ExoPlaybackException excep...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/RendererErrorMapper.java
  class RendererErrorMapper (line 21) | final class RendererErrorMapper {
    method RendererErrorMapper (line 23) | private RendererErrorMapper() {
    method map (line 27) | @SuppressWarnings({"PMD.StdCyclomaticComplexity", "PMD.CyclomaticCompl...
    method mapUnsupportedDrmException (line 99) | private static NoPlayer.PlayerError mapUnsupportedDrmException(Unsuppo...
    method mapCryptoException (line 110) | private static NoPlayer.PlayerError mapCryptoException(MediaCodec.Cryp...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/SourceErrorMapper.java
  class SourceErrorMapper (line 27) | final class SourceErrorMapper {
    method SourceErrorMapper (line 29) | private SourceErrorMapper() {
    method map (line 33) | @SuppressWarnings({"PMD.StdCyclomaticComplexity", "PMD.CyclomaticCompl...
    method mapAdsError (line 120) | private static NoPlayer.PlayerError mapAdsError(AdsMediaSource.AdLoadE...
    method mapClippingError (line 135) | private static NoPlayer.PlayerError mapClippingError(ClippingMediaSour...
    method mapHttpDataSourceException (line 164) | private static NoPlayer.PlayerError mapHttpDataSourceException(HttpDat...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/UnexpectedErrorMapper.java
  class UnexpectedErrorMapper (line 13) | final class UnexpectedErrorMapper {
    method UnexpectedErrorMapper (line 15) | private UnexpectedErrorMapper() {
    method map (line 19) | static NoPlayer.PlayerError map(RuntimeException unexpectedException, ...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/AnalyticsListenerForwarder.java
  class AnalyticsListenerForwarder (line 22) | class AnalyticsListenerForwarder implements AnalyticsListener {
    method AnalyticsListenerForwarder (line 26) | AnalyticsListenerForwarder(NoPlayer.InfoListener infoListeners) {
    method onPlayerStateChanged (line 30) | @Override
    method onTimelineChanged (line 41) | @Override
    method onPositionDiscontinuity (line 51) | @Override
    method onSeekStarted (line 61) | @Override
    method onSeekProcessed (line 70) | @Override
    method onPlaybackParametersChanged (line 79) | @Override
    method onRepeatModeChanged (line 89) | @Override
    method onShuffleModeChanged (line 99) | @Override
    method onLoadingChanged (line 109) | @Override
    method onPlayerError (line 119) | @Override
    method onTracksChanged (line 129) | @Override
    method onLoadStarted (line 140) | @Override
    method onLoadCompleted (line 153) | @Override
    method onLoadCanceled (line 166) | @Override
    method onLoadError (line 179) | @Override
    method onDownstreamFormatChanged (line 196) | @Override
    method onUpstreamDiscarded (line 206) | @Override
    method onMediaPeriodCreated (line 216) | @Override
    method onMediaPeriodReleased (line 225) | @Override
    method onReadingStarted (line 234) | @Override
    method onBandwidthEstimate (line 243) | @Override
    method onSurfaceSizeChanged (line 258) | @Override
    method onMetadata (line 269) | @Override
    method onDecoderEnabled (line 279) | @Override
    method onDecoderInitialized (line 290) | @Override
    method onDecoderInputFormatChanged (line 305) | @Override
    method onDecoderDisabled (line 316) | @Override
    method onAudioSessionId (line 327) | @Override
    method onAudioUnderrun (line 337) | @Override
    method onDroppedVideoFrames (line 349) | @Override
    method onVideoSizeChanged (line 360) | @Override
    method onRenderedFirstFrame (line 377) | @Override
    method onDrmKeysLoaded (line 387) | @Override
    method onDrmSessionManagerError (line 396) | @Override
    method onDrmKeysRestored (line 406) | @Override
    method onDrmKeysRemoved (line 415) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/BitrateForwarder.java
  class BitrateForwarder (line 13) | class BitrateForwarder implements MediaSourceEventListener {
    method BitrateForwarder (line 20) | BitrateForwarder(NoPlayer.BitrateChangedListener bitrateChangedListene...
    method onMediaPeriodCreated (line 24) | @Override
    method onMediaPeriodReleased (line 29) | @Override
    method onLoadStarted (line 34) | @Override
    method onLoadCompleted (line 42) | @Override
    method onLoadCanceled (line 50) | @Override
    method onLoadError (line 58) | @Override
    method onReadingStarted (line 68) | @Override
    method onUpstreamDiscarded (line 73) | @Override
    method onDownstreamFormatChanged (line 80) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/BufferStateForwarder.java
  class BufferStateForwarder (line 11) | class BufferStateForwarder implements Player.EventListener {
    method BufferStateForwarder (line 15) | BufferStateForwarder(NoPlayer.BufferStateListener bufferStateListener) {
    method onPlayerStateChanged (line 19) | @Override
    method onRepeatModeChanged (line 28) | @Override
    method onShuffleModeEnabledChanged (line 33) | @Override
    method onTimelineChanged (line 38) | @Override
    method onTracksChanged (line 43) | @Override
    method onLoadingChanged (line 48) | @Override
    method onPlayerError (line 53) | @Override
    method onPositionDiscontinuity (line 58) | @Override
    method onPlaybackParametersChanged (line 63) | @Override
    method onSeekProcessed (line 68) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/DrmSessionInfoForwarder.java
  class DrmSessionInfoForwarder (line 12) | class DrmSessionInfoForwarder implements DefaultDrmSessionEventListener {
    method DrmSessionInfoForwarder (line 16) | DrmSessionInfoForwarder(NoPlayer.InfoListener infoListener) {
    method onDrmKeysLoaded (line 20) | @Override
    method onDrmSessionManagerError (line 25) | @Override
    method onDrmKeysRestored (line 34) | @Override
    method onDrmKeysRemoved (line 40) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/EventInfoForwarder.java
  class EventInfoForwarder (line 17) | class EventInfoForwarder implements Player.EventListener {
    method EventInfoForwarder (line 21) | EventInfoForwarder(NoPlayer.InfoListener infoListener) {
    method onTimelineChanged (line 25) | @Override
    method onTracksChanged (line 36) | @Override
    method onLoadingChanged (line 46) | @Override
    method onPlayerStateChanged (line 55) | @Override
    method onRepeatModeChanged (line 65) | @Override
    method onShuffleModeEnabledChanged (line 74) | @Override
    method onPlayerError (line 83) | @Override
    method onPositionDiscontinuity (line 92) | @Override
    method onPlaybackParametersChanged (line 101) | @Override
    method onSeekProcessed (line 110) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/EventListener.java
  class EventListener (line 13) | class EventListener implements Player.EventListener {
    method add (line 17) | public void add(Player.EventListener listener) {
    method onTimelineChanged (line 21) | @Override
    method onTracksChanged (line 28) | @Override
    method onLoadingChanged (line 35) | @Override
    method onPlayerStateChanged (line 42) | @Override
    method onRepeatModeChanged (line 49) | @Override
    method onShuffleModeEnabledChanged (line 56) | @Override
    method onPlayerError (line 63) | @Override
    method onPositionDiscontinuity (line 70) | @Override
    method onPlaybackParametersChanged (line 77) | @Override
    method onSeekProcessed (line 84) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ExoPlayerDrmSessionEventListener.java
  class ExoPlayerDrmSessionEventListener (line 8) | class ExoPlayerDrmSessionEventListener implements DefaultDrmSessionEvent...
    method add (line 12) | void add(DefaultDrmSessionEventListener listener) {
    method onDrmKeysLoaded (line 16) | @Override
    method onDrmSessionManagerError (line 23) | @Override
    method onDrmKeysRestored (line 30) | @Override
    method onDrmKeysRemoved (line 37) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ExoPlayerForwarder.java
  class ExoPlayerForwarder (line 10) | public class ExoPlayerForwarder {
    method ExoPlayerForwarder (line 18) | public ExoPlayerForwarder() {
    method exoPlayerEventListener (line 26) | public EventListener exoPlayerEventListener() {
    method mediaSourceEventListener (line 30) | public MediaSourceEventListener mediaSourceEventListener() {
    method videoListener (line 34) | public VideoListener videoListener() {
    method drmSessionEventListener (line 38) | public DefaultDrmSessionEventListener drmSessionEventListener() {
    method analyticsListener (line 42) | public AnalyticsListener analyticsListener() {
    method bind (line 46) | public void bind(NoPlayer.PreparedListener preparedListener, PlayerSta...
    method bind (line 50) | public void bind(NoPlayer.CompletionListener completionListener, NoPla...
    method bind (line 55) | public void bind(NoPlayer.ErrorListener errorListener) {
    method bind (line 59) | public void bind(NoPlayer.BufferStateListener bufferStateListener) {
    method bind (line 63) | public void bind(NoPlayer.VideoSizeChangedListener videoSizeChangedLis...
    method bind (line 67) | public void bind(NoPlayer.BitrateChangedListener bitrateChangedListene...
    method bind (line 71) | public void bind(NoPlayer.InfoListener infoListeners) {
    method bind (line 78) | public void bind(NoPlayer.DroppedVideoFramesListener droppedVideoFrame...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ExoPlayerVideoListener.java
  class ExoPlayerVideoListener (line 8) | class ExoPlayerVideoListener implements VideoListener {
    method add (line 12) | public void add(VideoListener listener) {
    method onVideoSizeChanged (line 16) | @Override
    method onRenderedFirstFrame (line 23) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ForwarderInformation.java
  class ForwarderInformation (line 3) | class ForwarderInformation {
    class Parameters (line 5) | static final class Parameters {
      method Parameters (line 7) | private Parameters() {
    class Methods (line 52) | static final class Methods {
      method Methods (line 54) | private Methods() {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/MediaSourceEventForwarder.java
  class MediaSourceEventForwarder (line 16) | @SuppressWarnings({"PMD.UnusedImports", "checkstyle:ParameterNumber", "P...
    method MediaSourceEventForwarder (line 23) | MediaSourceEventForwarder(NoPlayer.InfoListener infoListener) {
    method onMediaPeriodCreated (line 27) | @Override
    method onMediaPeriodReleased (line 37) | @Override
    method onLoadStarted (line 47) | @Override
    method onLoadCompleted (line 62) | @Override
    method onLoadCanceled (line 77) | @Override
    method onLoadError (line 92) | @Override
    method onReadingStarted (line 109) | @Override
    method onUpstreamDiscarded (line 119) | @Override
    method onDownstreamFormatChanged (line 132) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/NoPlayerAnalyticsListener.java
  class NoPlayerAnalyticsListener (line 20) | class NoPlayerAnalyticsListener implements AnalyticsListener {
    method add (line 25) | public void add(AnalyticsListener listener) {
    method add (line 29) | public void add(NoPlayer.DroppedVideoFramesListener listener) {
    method onPlayerStateChanged (line 33) | @Override
    method onTimelineChanged (line 40) | @Override
    method onPositionDiscontinuity (line 47) | @Override
    method onSeekStarted (line 54) | @Override
    method onSeekProcessed (line 61) | @Override
    method onPlaybackParametersChanged (line 68) | @Override
    method onRepeatModeChanged (line 75) | @Override
    method onShuffleModeChanged (line 82) | @Override
    method onLoadingChanged (line 89) | @Override
    method onPlayerError (line 96) | @Override
    method onTracksChanged (line 103) | @Override
    method onLoadStarted (line 110) | @Override
    method onLoadCompleted (line 119) | @Override
    method onLoadCanceled (line 128) | @Override
    method onLoadError (line 137) | @Override
    method onDownstreamFormatChanged (line 148) | @Override
    method onUpstreamDiscarded (line 156) | @Override
    method onMediaPeriodCreated (line 164) | @Override
    method onMediaPeriodReleased (line 171) | @Override
    method onReadingStarted (line 178) | @Override
    method onBandwidthEstimate (line 185) | @Override
    method onSurfaceSizeChanged (line 195) | @Override
    method onMetadata (line 202) | @Override
    method onDecoderEnabled (line 209) | @Override
    method onDecoderInitialized (line 216) | @Override
    method onDecoderInputFormatChanged (line 226) | @Override
    method onDecoderDisabled (line 235) | @Override
    method onAudioSessionId (line 242) | @Override
    method onAudioUnderrun (line 249) | @Override
    method onDroppedVideoFrames (line 256) | @Override
    method onVideoSizeChanged (line 267) | @Override
    method onRenderedFirstFrame (line 278) | @Override
    method onDrmKeysLoaded (line 285) | @Override
    method onDrmSessionManagerError (line 292) | @Override
    method onDrmKeysRestored (line 299) | @Override
    method onDrmKeysRemoved (line 306) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/NoPlayerMediaSourceEventListener.java
  class NoPlayerMediaSourceEventListener (line 12) | @SuppressWarnings({"checkstyle:ParameterNumber", "PMD.ExcessiveParameter...
    method add (line 17) | public void add(MediaSourceEventListener listener) {
    method onMediaPeriodCreated (line 21) | @Override
    method onMediaPeriodReleased (line 28) | @Override
    method onLoadStarted (line 35) | @Override
    method onLoadCompleted (line 45) | @Override
    method onLoadCanceled (line 55) | @Override
    method onLoadError (line 65) | @Override
    method onReadingStarted (line 76) | @Override
    method onUpstreamDiscarded (line 83) | @Override
    method onDownstreamFormatChanged (line 92) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/OnCompletionForwarder.java
  class OnCompletionForwarder (line 11) | class OnCompletionForwarder implements Player.EventListener {
    method OnCompletionForwarder (line 15) | OnCompletionForwarder(NoPlayer.CompletionListener completionListener) {
    method onPlayerStateChanged (line 19) | @Override
    method onRepeatModeChanged (line 26) | @Override
    method onShuffleModeEnabledChanged (line 31) | @Override
    method onTimelineChanged (line 36) | @Override
    method onTracksChanged (line 41) | @Override
    method onLoadingChanged (line 46) | @Override
    method onPlayerError (line 51) | @Override
    method onPositionDiscontinuity (line 56) | @Override
    method onPlaybackParametersChanged (line 61) | @Override
    method onSeekProcessed (line 66) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/OnCompletionStateChangedForwarder.java
  class OnCompletionStateChangedForwarder (line 11) | class OnCompletionStateChangedForwarder implements Player.EventListener {
    method OnCompletionStateChangedForwarder (line 15) | OnCompletionStateChangedForwarder(NoPlayer.StateChangedListener stateC...
    method onPlayerStateChanged (line 19) | @Override
    method onRepeatModeChanged (line 26) | @Override
    method onShuffleModeEnabledChanged (line 31) | @Override
    method onTimelineChanged (line 36) | @Override
    method onTracksChanged (line 41) | @Override
    method onLoadingChanged (line 46) | @Override
    method onPlayerError (line 51) | @Override
    method onPositionDiscontinuity (line 56) | @Override
    method onPlaybackParametersChanged (line 61) | @Override
    method onSeekProcessed (line 66) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/OnPrepareForwarder.java
  class OnPrepareForwarder (line 12) | class OnPrepareForwarder implements Player.EventListener {
    method OnPrepareForwarder (line 17) | OnPrepareForwarder(NoPlayer.PreparedListener preparedListener, PlayerS...
    method onPlayerStateChanged (line 22) | @Override
    method onRepeatModeChanged (line 29) | @Override
    method onShuffleModeEnabledChanged (line 34) | @Override
    method isReady (line 39) | private boolean isReady(int playbackState) {
    method onTimelineChanged (line 43) | @Override
    method onTracksChanged (line 48) | @Override
    method onLoadingChanged (line 53) | @Override
    method onPlayerError (line 58) | @Override
    method onPositionDiscontinuity (line 63) | @Override
    method onPlaybackParametersChanged (line 68) | @Override
    method onSeekProcessed (line 73) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/PlayerOnErrorForwarder.java
  class PlayerOnErrorForwarder (line 12) | class PlayerOnErrorForwarder implements Player.EventListener {
    method PlayerOnErrorForwarder (line 16) | PlayerOnErrorForwarder(NoPlayer.ErrorListener errorListener) {
    method onPlayerError (line 20) | @Override
    method onPositionDiscontinuity (line 26) | @Override
    method onTimelineChanged (line 31) | @Override
    method onTracksChanged (line 36) | @Override
    method onLoadingChanged (line 41) | @Override
    method onPlayerStateChanged (line 46) | @Override
    method onRepeatModeChanged (line 51) | @Override
    method onShuffleModeEnabledChanged (line 56) | @Override
    method onPlaybackParametersChanged (line 61) | @Override
    method onSeekProcessed (line 66) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/VideoSizeChangedForwarder.java
  class VideoSizeChangedForwarder (line 6) | class VideoSizeChangedForwarder implements VideoListener {
    method VideoSizeChangedForwarder (line 10) | VideoSizeChangedForwarder(NoPlayer.VideoSizeChangedListener videoSizeC...
    method onVideoSizeChanged (line 14) | @Override
    method onRenderedFirstFrame (line 19) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/AudioTrackType.java
  type AudioTrackType (line 3) | public enum AudioTrackType {
    method AudioTrackType (line 11) | AudioTrackType(int selectionFlag) {
    method from (line 15) | static AudioTrackType from(int selectionFlag) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerAudioTrackSelector.java
  class ExoPlayerAudioTrackSelector (line 16) | public class ExoPlayerAudioTrackSelector {
    method ExoPlayerAudioTrackSelector (line 20) | public ExoPlayerAudioTrackSelector(ExoPlayerTrackSelector trackSelecto...
    method selectAudioTrack (line 24) | public boolean selectAudioTrack(PlayerAudioTrack audioTrack, RendererT...
    method getAudioTracks (line 34) | public AudioTracks getAudioTracks(RendererTypeRequester rendererTypeRe...
    method clearAudioTrack (line 64) | public boolean clearAudioTrack(RendererTypeRequester rendererTypeReque...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerMappedTrackInfo.java
  class ExoPlayerMappedTrackInfo (line 6) | class ExoPlayerMappedTrackInfo {
    method ExoPlayerMappedTrackInfo (line 10) | ExoPlayerMappedTrackInfo(MappingTrackSelector.MappedTrackInfo mappedTr...
    method getTrackGroups (line 14) | TrackGroupArray getTrackGroups(int index) {
    method getAdaptiveSupport (line 18) | int getAdaptiveSupport(int rendererIndex, int groupIndex, boolean incl...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerSubtitleTrackSelector.java
  class ExoPlayerSubtitleTrackSelector (line 15) | public class ExoPlayerSubtitleTrackSelector {
    method ExoPlayerSubtitleTrackSelector (line 19) | public ExoPlayerSubtitleTrackSelector(ExoPlayerTrackSelector trackSele...
    method selectTextTrack (line 23) | public boolean selectTextTrack(PlayerSubtitleTrack subtitleTrack, Rend...
    method getSubtitleTracks (line 33) | public List<PlayerSubtitleTrack> getSubtitleTracks(RendererTypeRequest...
    method clearSubtitleTrack (line 59) | public boolean clearSubtitleTrack(RendererTypeRequester rendererTypeRe...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerTrackSelector.java
  class ExoPlayerTrackSelector (line 11) | @SuppressWarnings({"checkstyle:FinalClass", "PMD.ClassWithOnlyPrivateCon...
    method newInstance (line 17) | public static ExoPlayerTrackSelector newInstance(DefaultTrackSelector ...
    method ExoPlayerTrackSelector (line 22) | private ExoPlayerTrackSelector(DefaultTrackSelector trackSelector, Ren...
    method trackGroups (line 27) | TrackGroupArray trackGroups(TrackType trackType, RendererTypeRequester...
    method clearSelectionOverrideFor (line 32) | boolean clearSelectionOverrideFor(TrackType trackType, RendererTypeReq...
    method trackInfo (line 45) | private ExoPlayerMappedTrackInfo trackInfo() {
    method mappedTrackInfoLength (line 54) | private int mappedTrackInfoLength() {
    method setSelectionOverride (line 58) | boolean setSelectionOverride(TrackType trackType,
    method supportsTrackSwitching (line 74) | boolean supportsTrackSwitching(TrackType trackType,
    method clearMaxVideoBitrate (line 84) | void clearMaxVideoBitrate() {
    method setMaxVideoBitrate (line 88) | void setMaxVideoBitrate(int maxVideoBitrate) {
    method setMaxVideoBitrateParameter (line 92) | private void setMaxVideoBitrateParameter(int maxValue) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerVideoTrackSelector.java
  class ExoPlayerVideoTrackSelector (line 18) | public class ExoPlayerVideoTrackSelector {
    method ExoPlayerVideoTrackSelector (line 22) | public ExoPlayerVideoTrackSelector(ExoPlayerTrackSelector trackSelecto...
    method selectVideoTrack (line 26) | public boolean selectVideoTrack(PlayerVideoTrack videoTrack, RendererT...
    method getVideoTracks (line 36) | public List<PlayerVideoTrack> getVideoTracks(RendererTypeRequester ren...
    method getSelectedVideoTrack (line 65) | public Optional<PlayerVideoTrack> getSelectedVideoTrack(SimpleExoPlaye...
    method findSelectedVideoTrack (line 78) | private Optional<PlayerVideoTrack> findSelectedVideoTrack(Format selec...
    method clearVideoTrack (line 87) | public boolean clearVideoTrack(RendererTypeRequester rendererTypeReque...
    method clearMaxVideoBitrate (line 91) | public void clearMaxVideoBitrate() {
    method setMaxVideoBitrate (line 95) | public void setMaxVideoBitrate(int maxVideoBitrate) {

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/MediaSourceFactory.java
  class MediaSourceFactory (line 22) | public class MediaSourceFactory {
    method MediaSourceFactory (line 30) | public MediaSourceFactory(Context context,
    method create (line 42) | public MediaSource create(Options options,
    method createDataSourceFactory (line 59) | private DefaultDataSourceFactory createDataSourceFactory(DefaultBandwi...
    method createHlsMediaSource (line 75) | private MediaSource createHlsMediaSource(DefaultDataSourceFactory defa...
    method createH264MediaSource (line 84) | private MediaSource createH264MediaSource(DefaultDataSourceFactory def...
    method createDashMediaSource (line 95) | private MediaSource createDashMediaSource(DefaultDataSourceFactory def...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/RendererTrackIndexExtractor.java
  class RendererTrackIndexExtractor (line 7) | class RendererTrackIndexExtractor {
    method extract (line 9) | Optional<Integer> extract(TrackType trackType, int numberOfTracks, Ren...

FILE: core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/TrackType.java
  type TrackType (line 3) | enum TrackType {

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/BitrateChangedListeners.java
  class BitrateChangedListeners (line 9) | class BitrateChangedListeners implements NoPlayer.BitrateChangedListener {
    method add (line 13) | void add(NoPlayer.BitrateChangedListener listener) {
    method remove (line 17) | void remove(NoPlayer.BitrateChangedListener listener) {
    method clear (line 21) | void clear() {
    method onBitrateChanged (line 25) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/BufferStateListeners.java
  class BufferStateListeners (line 8) | class BufferStateListeners implements NoPlayer.BufferStateListener {
    method add (line 12) | void add(NoPlayer.BufferStateListener listener) {
    method remove (line 16) | void remove(NoPlayer.BufferStateListener listener) {
    method clear (line 20) | void clear() {
    method onBufferStarted (line 24) | @Override
    method onBufferCompleted (line 31) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/CompletionListeners.java
  class CompletionListeners (line 8) | class CompletionListeners implements NoPlayer.CompletionListener {
    method add (line 14) | void add(NoPlayer.CompletionListener listener) {
    method remove (line 18) | void remove(NoPlayer.CompletionListener listener) {
    method clear (line 22) | void clear() {
    method onCompletion (line 26) | public void onCompletion() {
    method resetCompletedState (line 35) | void resetCompletedState() {

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/DroppedFramesListeners.java
  class DroppedFramesListeners (line 8) | public class DroppedFramesListeners implements NoPlayer.DroppedVideoFram...
    method add (line 12) | void add(NoPlayer.DroppedVideoFramesListener listener) {
    method remove (line 16) | void remove(NoPlayer.DroppedVideoFramesListener listener) {
    method clear (line 20) | void clear() {
    method onDroppedVideoFrames (line 24) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/ErrorListeners.java
  class ErrorListeners (line 8) | class ErrorListeners implements NoPlayer.ErrorListener {
    method add (line 12) | void add(NoPlayer.ErrorListener listener) {
    method remove (line 16) | void remove(NoPlayer.ErrorListener listener) {
    method clear (line 20) | void clear() {
    method onError (line 24) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/HeartbeatCallbacks.java
  class HeartbeatCallbacks (line 8) | class HeartbeatCallbacks implements NoPlayer.HeartbeatCallback {
    method registerCallback (line 12) | void registerCallback(NoPlayer.HeartbeatCallback heartbeatCallback) {
    method clear (line 16) | void clear() {
    method onBeat (line 20) | @Override
    method unregisterCallback (line 27) | void unregisterCallback(NoPlayer.HeartbeatCallback heartbeatCallback) {

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/InfoListeners.java
  class InfoListeners (line 9) | class InfoListeners implements NoPlayer.InfoListener {
    method add (line 13) | void add(NoPlayer.InfoListener listener) {
    method remove (line 17) | void remove(NoPlayer.InfoListener listener) {
    method clear (line 21) | void clear() {
    method onNewInfo (line 25) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/PlayerListenersHolder.java
  class PlayerListenersHolder (line 7) | public class PlayerListenersHolder implements Listeners {
    method PlayerListenersHolder (line 21) | public PlayerListenersHolder() {
    method addErrorListener (line 34) | @Override
    method removeErrorListener (line 39) | @Override
    method addPreparedListener (line 44) | @Override
    method removePreparedListener (line 49) | @Override
    method addBufferStateListener (line 54) | @Override
    method removeBufferStateListener (line 59) | @Override
    method addCompletionListener (line 64) | @Override
    method removeCompletionListener (line 69) | @Override
    method addStateChangedListener (line 74) | @Override
    method removeStateChangedListener (line 79) | @Override
    method addInfoListener (line 84) | @Override
    method removeInfoListener (line 89) | @Override
    method addBitrateChangedListener (line 94) | @Override
    method removeBitrateChangedListener (line 99) | @Override
    method addHeartbeatCallback (line 104) | @Override
    method removeHeartbeatCallback (line 109) | @Override
    method addVideoSizeChangedListener (line 114) | @Override
    method removeVideoSizeChangedListener (line 119) | @Override
    method addDroppedVideoFrames (line 124) | @Override
    method removeDroppedVideoFrames (line 129) | @Override
    method getErrorListeners (line 134) | public NoPlayer.ErrorListener getErrorListeners() {
    method getPreparedListeners (line 138) | public NoPlayer.PreparedListener getPreparedListeners() {
    method getBufferStateListeners (line 142) | public NoPlayer.BufferStateListener getBufferStateListeners() {
    method getCompletionListeners (line 146) | public NoPlayer.CompletionListener getCompletionListeners() {
    method getStateChangedListeners (line 150) | public NoPlayer.StateChangedListener getStateChangedListeners() {
    method getInfoListeners (line 154) | public NoPlayer.InfoListener getInfoListeners() {
    method getHeartbeatCallbacks (line 158) | public NoPlayer.HeartbeatCallback getHeartbeatCallbacks() {
    method getVideoSizeChangedListeners (line 162) | public NoPlayer.VideoSizeChangedListener getVideoSizeChangedListeners() {
    method getBitrateChangedListeners (line 166) | public NoPlayer.BitrateChangedListener getBitrateChangedListeners() {
    method getDroppedVideoFramesListeners (line 170) | public NoPlayer.DroppedVideoFramesListener getDroppedVideoFramesListen...
    method resetState (line 174) | public void resetState() {
    method clear (line 179) | public void clear() {

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/PreparedListeners.java
  class PreparedListeners (line 9) | class PreparedListeners implements NoPlayer.PreparedListener {
    method add (line 15) | void add(NoPlayer.PreparedListener listener) {
    method remove (line 19) | void remove(NoPlayer.PreparedListener listener) {
    method clear (line 23) | void clear() {
    method onPrepared (line 27) | @Override
    method resetPreparedState (line 37) | void resetPreparedState() {

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/StateChangedListeners.java
  class StateChangedListeners (line 9) | class StateChangedListeners implements NoPlayer.StateChangedListener {
    type State (line 11) | private enum State {
    method add (line 21) | void add(NoPlayer.StateChangedListener listener) {
    method remove (line 25) | void remove(NoPlayer.StateChangedListener listener) {
    method clear (line 29) | void clear() {
    method onVideoPlaying (line 33) | @Override
    method onVideoPaused (line 47) | @Override
    method onVideoStopped (line 61) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/listeners/VideoSizeChangedListeners.java
  class VideoSizeChangedListeners (line 8) | class VideoSizeChangedListeners implements NoPlayer.VideoSizeChangedList...
    method add (line 12) | void add(NoPlayer.VideoSizeChangedListener listener) {
    method remove (line 16) | void remove(NoPlayer.VideoSizeChangedListener listener) {
    method clear (line 20) | void clear() {
    method onVideoSizeChanged (line 24) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerAudioTrackSelector.java
  class AndroidMediaPlayerAudioTrackSelector (line 12) | class AndroidMediaPlayerAudioTrackSelector {
    method AndroidMediaPlayerAudioTrackSelector (line 21) | AndroidMediaPlayerAudioTrackSelector(TrackInfosFactory trackInfosFacto...
    method getAudioTracks (line 25) | AudioTracks getAudioTracks(MediaPlayer mediaPlayer) {
    method selectAudioTrack (line 53) | boolean selectAudioTrack(MediaPlayer mediaPlayer, PlayerAudioTrack pla...

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerFacade.java
  class AndroidMediaPlayerFacade (line 30) | @SuppressWarnings("PMD.GodClass")
    method newInstance (line 50) | static AndroidMediaPlayerFacade newInstance(Context context, MediaPlay...
    method AndroidMediaPlayerFacade (line 59) | AndroidMediaPlayerFacade(Context context,
    method prepareVideo (line 73) | void prepareVideo(Uri videoUri, Either<Surface, SurfaceHolder> surface) {
    method requestAudioFocus (line 85) | private void requestAudioFocus() {
    method createAndBindMediaPlayer (line 89) | private MediaPlayer createAndBindMediaPlayer(Either<Surface, SurfaceHo...
    method reportCreationError (line 108) | private void reportCreationError(Exception ex, Uri videoUri) {
    method onVideoSizeChanged (line 115) | @Override
    method onPrepared (line 126) | @Override
    method onCompletion (line 139) | @Override
    method onError (line 151) | @Override
    method onBufferingUpdate (line 164) | @Override
    method release (line 170) | void release() {
    method start (line 179) | void start(Either<Surface, SurfaceHolder> surface) throws IllegalState...
    method attachSurface (line 186) | private void attachSurface(final MediaPlayer mediaPlayer, Either<Surfa...
    method pause (line 202) | void pause() throws IllegalStateException {
    method mediaDurationInMillis (line 211) | int mediaDurationInMillis() throws IllegalStateException {
    method currentPositionInMillis (line 216) | int currentPositionInMillis() throws IllegalStateException {
    method seekTo (line 221) | void seekTo(long positionInMillis) throws IllegalStateException {
    method isPlaying (line 226) | boolean isPlaying() {
    method getBufferPercentage (line 230) | int getBufferPercentage() throws IllegalStateException {
    method getAudioTracks (line 235) | AudioTracks getAudioTracks() throws IllegalStateException {
    method selectAudioTrack (line 240) | boolean selectAudioTrack(PlayerAudioTrack playerAudioTrack) throws Ill...
    method clearAudioTrackSelection (line 245) | boolean clearAudioTrackSelection() {
    method setOnSeekCompleteListener (line 251) | void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener seek...
    method hasPlayedContent (line 256) | boolean hasPlayedContent() {
    method hasPlayer (line 260) | private boolean hasPlayer() {
    method clearSubtitleTrack (line 264) | boolean clearSubtitleTrack() throws IllegalStateException {
    method selectSubtitleTrack (line 270) | boolean selectSubtitleTrack(PlayerSubtitleTrack subtitleTrack) throws ...
    method getSubtitleTracks (line 276) | List<PlayerSubtitleTrack> getSubtitleTracks() throws IllegalStateExcep...
    method assertIsInPlaybackState (line 282) | private void assertIsInPlaybackState() throws IllegalStateException {
    method getSelectedVideoTrack (line 288) | Optional<PlayerVideoTrack> getSelectedVideoTrack() {
    method getVideoTracks (line 294) | List<PlayerVideoTrack> getVideoTracks() {
    method selectVideoTrack (line 300) | boolean selectVideoTrack(PlayerVideoTrack videoTrack) {
    method clearVideoTrackSelection (line 306) | boolean clearVideoTrackSelection() {
    method setRepeating (line 312) | void setRepeating(boolean repeating) {
    method setVolume (line 317) | void setVolume(float volume) {
    method getVolume (line 323) | float getVolume() {
    method clearMaxVideoBitrate (line 328) | void clearMaxVideoBitrate() {
    method setMaxVideoBitrate (line 333) | void setMaxVideoBitrate(int maxVideoBitrate) {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerImpl.java
  class AndroidMediaPlayerImpl (line 33) | @SuppressWarnings("PMD.GodClass")
    method AndroidMediaPlayerImpl (line 59) | @SuppressWarnings("checkstyle:ParameterNumber")
    method initialise (line 81) | void initialise() {
    method onSeekComplete (line 116) | @Override
    method setRepeating (line 127) | @Override
    method setVolume (line 132) | @Override
    method getVolume (line 137) | @Override
    method getListeners (line 142) | @Override
    method play (line 147) | @Override
    method playAt (line 159) | @Override
    method initialSeekWorkaround (line 177) | private void initialSeekWorkaround(Either<Surface, SurfaceHolder> surf...
    method initialisePlaybackForSeeking (line 188) | private void initialisePlaybackForSeeking(Either<Surface, SurfaceHolde...
    method requestSurface (line 193) | private void requestSurface(SurfaceRequester.Callback callback) {
    method seekWithIntentToPlay (line 201) | private void seekWithIntentToPlay(long positionInMillis) throws Illega...
    method isPlaying (line 206) | @Override
    method seekTo (line 211) | @Override
    method pause (line 217) | @Override
    method loadVideo (line 227) | @Override
    method createSurfaceByShowingVideoContainer (line 243) | private void createSurfaceByShowingVideoContainer() {
    method assertPlayerViewIsAttached (line 247) | private void assertPlayerViewIsAttached() {
    method loadVideoWithTimeout (line 253) | @Override
    method playheadPositionInMillis (line 259) | @Override
    method isSeeking (line 264) | private boolean isSeeking() {
    method mediaDurationInMillis (line 268) | @Override
    method bufferPercentage (line 273) | @Override
    method videoWidth (line 278) | @Override
    method videoHeight (line 283) | @Override
    method getPlayerInformation (line 288) | @Override
    method attach (line 293) | @Override
    method detach (line 303) | @Override
    method clearSurfaceHolderCallbacks (line 313) | private void clearSurfaceHolderCallbacks() {
    method selectAudioTrack (line 320) | @Override
    method clearAudioTrackSelection (line 325) | @Override
    method showSubtitleTrack (line 330) | @Override
    method hideSubtitleTrack (line 335) | @Override
    method getAudioTracks (line 340) | @Override
    method selectVideoTrack (line 345) | @Override
    method getSelectedVideoTrack (line 350) | @Override
    method clearVideoTrackSelection (line 355) | @Override
    method getVideoTracks (line 360) | @Override
    method getSubtitleTracks (line 365) | @Override
    method clearMaxVideoBitrate (line 370) | @Override
    method setMaxVideoBitrate (line 375) | @Override
    method stop (line 380) | @Override
    method release (line 386) | @Override
    method reset (line 392) | private void reset() {
    method destroySurfaceByHidingVideoContainer (line 401) | private void destroySurfaceByHidingVideoContainer() {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerType.java
  type AndroidMediaPlayerType (line 3) | enum AndroidMediaPlayerType {
    method AndroidMediaPlayerType (line 11) | AndroidMediaPlayerType(String name) {
    method getName (line 15) | String getName() {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/BuggyVideoDriverPreventer.java
  class BuggyVideoDriverPreventer (line 14) | class BuggyVideoDriverPreventer {
    method BuggyVideoDriverPreventer (line 20) | BuggyVideoDriverPreventer(MediaPlayerTypeReader mediaPlayerTypeReader) {
    method preventVideoDriverBug (line 24) | void preventVideoDriverBug(NoPlayer player, View containerView) {
    method videoDriverCanBeBuggy (line 30) | private boolean videoDriverCanBeBuggy() {
    method attemptToCorrectMediaPlayerStatus (line 34) | private void attemptToCorrectMediaPlayerStatus(NoPlayer player, View c...
    method clear (line 39) | void clear(View containerView) {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/CheckBufferHeartbeatCallback.java
  class CheckBufferHeartbeatCallback (line 5) | public class CheckBufferHeartbeatCallback implements NoPlayer.HeartbeatC...
    method bind (line 13) | public void bind(BufferListener bufferListener) {
    method onBeat (line 17) | @Override
    method positionNotUpdating (line 37) | private boolean positionNotUpdating(long currentPositionInMillis) {
    method stopBuffering (line 41) | private void stopBuffering() {
    method startBuffering (line 45) | private void startBuffering() {
    method mediaPlayerIsUnavailable (line 49) | private boolean mediaPlayerIsUnavailable(NoPlayer player) {
    type BufferListener (line 58) | public interface BufferListener {
      method onBufferStart (line 60) | void onBufferStart();
      method onBufferComplete (line 62) | void onBufferComplete();
      method onBufferStart (line 65) | @Override
      method onBufferComplete (line 70) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/DelayedActionExecutor.java
  class DelayedActionExecutor (line 8) | class DelayedActionExecutor {
    method DelayedActionExecutor (line 13) | DelayedActionExecutor(Handler handler, Map<Action, Runnable> runnables) {
    method performAfterDelay (line 18) | void performAfterDelay(final Action action, long delayInMillis) {
    method clearAllActions (line 30) | void clearAllActions() {
    type Action (line 39) | interface Action {
      method perform (line 41) | void perform();

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/ErrorFactory.java
  class ErrorFactory (line 10) | public final class ErrorFactory {
    method ErrorFactory (line 12) | private ErrorFactory() {
    method createErrorFrom (line 16) | @SuppressWarnings({"PMD.StdCyclomaticComplexity", "PMD.CyclomaticCompl...

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/ErrorFormatter.java
  class ErrorFormatter (line 3) | final class ErrorFormatter {
    method ErrorFormatter (line 5) | private ErrorFormatter() {
    method formatMessage (line 8) | static String formatMessage(int type, int extra) {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/MediaPlayerCreator.java
  class MediaPlayerCreator (line 5) | class MediaPlayerCreator {
    method createMediaPlayer (line 7) | MediaPlayer createMediaPlayer() {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/MediaPlayerInformation.java
  class MediaPlayerInformation (line 8) | class MediaPlayerInformation implements PlayerInformation {
    method MediaPlayerInformation (line 12) | MediaPlayerInformation(MediaPlayerTypeReader mediaPlayerTypeReader) {
    method getPlayerType (line 16) | @Override
    method getVersion (line 21) | @Override
    method getName (line 26) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/MediaPlayerTypeReader.java
  class MediaPlayerTypeReader (line 5) | class MediaPlayerTypeReader {
    method MediaPlayerTypeReader (line 14) | MediaPlayerTypeReader(SystemProperties systemProperties, int deviceOSV...
    method getPlayerType (line 19) | AndroidMediaPlayerType getPlayerType() {
    method getMediaPlayerType (line 29) | private AndroidMediaPlayerType getMediaPlayerType() throws SystemPrope...
    method getPlayerTypeLollipop (line 33) | private AndroidMediaPlayerType getPlayerTypeLollipop() throws SystemPr...
    method getPlayerTypePreLollipop (line 40) | private AndroidMediaPlayerType getPlayerTypePreLollipop() throws Syste...
    method getBooleanProp (line 46) | private boolean getBooleanProp(String prop) throws SystemProperties.Mi...

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/NoPlayerMediaPlayerCreator.java
  class NoPlayerMediaPlayerCreator (line 16) | public class NoPlayerMediaPlayerCreator {
    method newInstance (line 20) | public static NoPlayerMediaPlayerCreator newInstance(Handler handler) {
    method NoPlayerMediaPlayerCreator (line 25) | NoPlayerMediaPlayerCreator(InternalCreator internalCreator) {
    method createMediaPlayer (line 29) | public NoPlayer createMediaPlayer(Context context) {
    class InternalCreator (line 35) | static class InternalCreator {
      method InternalCreator (line 39) | InternalCreator(Handler handler) {
      method create (line 43) | public AndroidMediaPlayerImpl create(Context context) {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/NoPlayerTrackInfo.java
  class NoPlayerTrackInfo (line 5) | class NoPlayerTrackInfo {
    method NoPlayerTrackInfo (line 9) | NoPlayerTrackInfo(MediaPlayer.TrackInfo trackInfo) {
    method type (line 13) | int type() {
    method language (line 17) | String language() {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/NoPlayerTrackInfos.java
  class NoPlayerTrackInfos (line 5) | class NoPlayerTrackInfos {
    method NoPlayerTrackInfos (line 9) | NoPlayerTrackInfos(List<NoPlayerTrackInfo> trackInfos) {
    method get (line 13) | NoPlayerTrackInfo get(int index) {
    method size (line 17) | int size() {
    method equals (line 21) | @Override
    method hashCode (line 35) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/OnPotentialBuggyDriverLayoutListener.java
  class OnPotentialBuggyDriverLayoutListener (line 7) | class OnPotentialBuggyDriverLayoutListener implements View.OnLayoutChang...
    method OnPotentialBuggyDriverLayoutListener (line 11) | OnPotentialBuggyDriverLayoutListener(NoPlayer player) {
    method onLayoutChange (line 15) | @SuppressWarnings("checkstyle:parameternumber") // Checkstyle should n...
    method statusMightBeCorrupted (line 23) | private boolean statusMightBeCorrupted() {
    method forceAlignNativeMediaPlayerStatus (line 27) | private void forceAlignNativeMediaPlayerStatus() {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/PlaybackStateChecker.java
  class PlaybackStateChecker (line 9) | class PlaybackStateChecker {
    method isPlaying (line 11) | boolean isPlaying(MediaPlayer mediaPlayer, PlaybackState playbackState) {
    method isInPlaybackState (line 15) | boolean isInPlaybackState(MediaPlayer mediaPlayer, PlaybackState playb...
    type PlaybackState (line 22) | enum PlaybackState {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/SystemProperties.java
  class SystemProperties (line 8) | class SystemProperties {
    method get (line 15) | @SuppressLint("PrivateApi") // This method uses reflection to call and...
    class MissingSystemPropertiesException (line 32) | static class MissingSystemPropertiesException extends Exception {
      method MissingSystemPropertiesException (line 33) | MissingSystemPropertiesException(Exception e) {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/TrackInfosFactory.java
  class TrackInfosFactory (line 8) | class TrackInfosFactory {
    method createFrom (line 10) | NoPlayerTrackInfos createFrom(MediaPlayer mediaPlayer) {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/BufferHeartbeatListener.java
  class BufferHeartbeatListener (line 6) | class BufferHeartbeatListener implements CheckBufferHeartbeatCallback.Bu...
    method BufferHeartbeatListener (line 10) | BufferHeartbeatListener(NoPlayer.BufferStateListener bufferStateListen...
    method onBufferStart (line 14) | @Override
    method onBufferComplete (line 19) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/BufferInfoForwarder.java
  class BufferInfoForwarder (line 8) | class BufferInfoForwarder implements CheckBufferHeartbeatCallback.Buffer...
    method BufferInfoForwarder (line 12) | BufferInfoForwarder(NoPlayer.InfoListener infoListener) {
    method onBufferStart (line 16) | @Override
    method onBufferComplete (line 23) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/BufferOnPreparedListener.java
  class BufferOnPreparedListener (line 7) | class BufferOnPreparedListener implements MediaPlayer.OnPreparedListener {
    method BufferOnPreparedListener (line 11) | BufferOnPreparedListener(NoPlayer.BufferStateListener bufferStateListe...
    method onPrepared (line 15) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/CompletionForwarder.java
  class CompletionForwarder (line 7) | class CompletionForwarder implements MediaPlayer.OnCompletionListener {
    method CompletionForwarder (line 11) | CompletionForwarder(NoPlayer.CompletionListener completionListener) {
    method onCompletion (line 15) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/CompletionInfoForwarder.java
  class CompletionInfoForwarder (line 9) | class CompletionInfoForwarder implements MediaPlayer.OnCompletionListener {
    method CompletionInfoForwarder (line 13) | CompletionInfoForwarder(NoPlayer.InfoListener infoListener) {
    method onCompletion (line 17) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/CompletionStateChangedForwarder.java
  class CompletionStateChangedForwarder (line 7) | class CompletionStateChangedForwarder implements MediaPlayer.OnCompletio...
    method CompletionStateChangedForwarder (line 11) | CompletionStateChangedForwarder(NoPlayer.StateChangedListener stateCha...
    method onCompletion (line 15) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/ErrorForwarder.java
  class ErrorForwarder (line 8) | class ErrorForwarder implements MediaPlayer.OnErrorListener {
    method ErrorForwarder (line 13) | ErrorForwarder(NoPlayer.BufferStateListener bufferStateListener, NoPla...
    method onError (line 18) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/ErrorInfoForwarder.java
  class ErrorInfoForwarder (line 9) | class ErrorInfoForwarder implements MediaPlayer.OnErrorListener {
    method ErrorInfoForwarder (line 13) | ErrorInfoForwarder(NoPlayer.InfoListener infoListener) {
    method onError (line 17) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/HeartBeatListener.java
  class HeartBeatListener (line 8) | class HeartBeatListener implements CheckBufferHeartbeatCallback.BufferLi...
    method add (line 12) | void add(CheckBufferHeartbeatCallback.BufferListener listener) {
    method onBufferStart (line 16) | @Override
    method onBufferComplete (line 23) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerCompletionListener.java
  class MediaPlayerCompletionListener (line 8) | class MediaPlayerCompletionListener implements MediaPlayer.OnCompletionL...
    method add (line 12) | void add(MediaPlayer.OnCompletionListener listener) {
    method onCompletion (line 16) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerErrorListener.java
  class MediaPlayerErrorListener (line 8) | class MediaPlayerErrorListener implements MediaPlayer.OnErrorListener {
    method add (line 12) | void add(MediaPlayer.OnErrorListener listener) {
    method onError (line 16) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerForwarder.java
  class MediaPlayerForwarder (line 9) | public class MediaPlayerForwarder {
    method MediaPlayerForwarder (line 17) | public MediaPlayerForwarder() {
    method bind (line 25) | public void bind(NoPlayer.PreparedListener preparedListener, PlayerSta...
    method bind (line 29) | public void bind(NoPlayer.BufferStateListener bufferStateListener, NoP...
    method bind (line 35) | public void bind(NoPlayer.CompletionListener completionListener, NoPla...
    method bind (line 40) | public void bind(NoPlayer.VideoSizeChangedListener videoSizeChangedLis...
    method bind (line 44) | public void bind(NoPlayer.InfoListener infoListener) {
    method onPreparedListener (line 52) | public MediaPlayer.OnPreparedListener onPreparedListener() {
    method onHeartbeatListener (line 56) | public CheckBufferHeartbeatCallback.BufferListener onHeartbeatListener...
    method onCompletionListener (line 60) | public MediaPlayer.OnCompletionListener onCompletionListener() {
    method onErrorListener (line 64) | public MediaPlayer.OnErrorListener onErrorListener() {
    method onSizeChangedListener (line 68) | public MediaPlayer.OnVideoSizeChangedListener onSizeChangedListener() {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerPreparedListener.java
  class MediaPlayerPreparedListener (line 8) | class MediaPlayerPreparedListener implements MediaPlayer.OnPreparedListe...
    method add (line 12) | void add(MediaPlayer.OnPreparedListener listener) {
    method onPrepared (line 16) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/OnPreparedForwarder.java
  class OnPreparedForwarder (line 8) | class OnPreparedForwarder implements MediaPlayer.OnPreparedListener {
    method OnPreparedForwarder (line 13) | OnPreparedForwarder(NoPlayer.PreparedListener preparedListener, Player...
    method onPrepared (line 18) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/OnPreparedInfoForwarder.java
  class OnPreparedInfoForwarder (line 9) | class OnPreparedInfoForwarder implements MediaPlayer.OnPreparedListener {
    method OnPreparedInfoForwarder (line 13) | OnPreparedInfoForwarder(NoPlayer.InfoListener infoListener) {
    method onPrepared (line 17) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/VideoSizeChangedForwarder.java
  class VideoSizeChangedForwarder (line 8) | class VideoSizeChangedForwarder implements MediaPlayer.OnVideoSizeChange...
    method VideoSizeChangedForwarder (line 15) | VideoSizeChangedForwarder(NoPlayer.VideoSizeChangedListener videoSizeC...
    method onVideoSizeChanged (line 19) | @Override
    method bothDimensionsHaveChanged (line 30) | private boolean bothDimensionsHaveChanged(int width, int height) {

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/VideoSizeChangedInfoForwarder.java
  class VideoSizeChangedInfoForwarder (line 9) | class VideoSizeChangedInfoForwarder implements MediaPlayer.OnVideoSizeCh...
    method VideoSizeChangedInfoForwarder (line 13) | VideoSizeChangedInfoForwarder(NoPlayer.InfoListener infoListener) {
    method onVideoSizeChanged (line 17) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/VideoSizeChangedListener.java
  class VideoSizeChangedListener (line 8) | public class VideoSizeChangedListener implements MediaPlayer.OnVideoSize...
    method add (line 12) | public void add(MediaPlayer.OnVideoSizeChangedListener listener) {
    method onVideoSizeChanged (line 16) | @Override

FILE: core/src/main/java/com/novoda/noplayer/internal/utils/AndroidDeviceVersion.java
  class AndroidDeviceVersion (line 5) | public class AndroidDeviceVersion {
    method newInstance (line 9) | public static AndroidDeviceVersion newInstance() {
    method AndroidDeviceVersion (line 13) | public AndroidDeviceVersion(int sdkInt) {
    method isJellyBeanEighteenOrAbove (line 17) | public boolean isJellyBeanEighteenOrAbove() {
    method isLollipopTwentyOneOrAbove (line 21) | public boolean isLollipopTwentyOneOrAbove() {
    method sdkInt (line 25) | public int sdkInt() {

FILE: core/src/main/java/com/novoda/noplayer/internal/utils/NoPlayerLog.java
  class NoPlayerLog (line 7) | @SuppressWarnings("PMD.ShortMethodName")    // This is a logger class, t...
    method NoPlayerLog (line 17) | private NoPlayerLog() {
    method setLoggingEnabled (line 21) | public static void setLoggingEnabled(boolean enabled) {
    method logMessage (line 25) | private static String logMessage(String message, Throwable throwable) {
    method getDetailedLog (line 33) | private static String getDetailedLog(String message) {
    method getStackTraceString (line 46) | private static String getStackTraceString(Throwable throwable) {
    method d (line 57) | public static void d(String msg) {
    method d (line 64) | public static void d(Throwable throwable, String msg) {
    method e (line 71) | public static void e(String msg) {
    method e (line 78) | public static void e(Throwable throwable, String msg) {
    method i (line 85) | public static void i(String msg) {
    method i (line 92) | public static void i(Throwable throwable, String msg) {
    method v (line 99) | public static void v(String msg) {
    method v (line 106) | public static void v(Throwable throwable, String msg) {
    method w (line 113) | public static void w(String msg) {
    method w (line 120) | public static void w(Throwable throwable, String msg) {
    method wtf (line 127) | public static void wtf(String msg) {
    method wtf (line 134) | public static void wtf(Throwable throwable) {
    method wtf (line 141) | public static void wtf(Throwable throwable, String msg) {

FILE: core/src/main/java/com/novoda/noplayer/internal/utils/Optional.java
  class Optional (line 3) | public final class Optional<T> {
    method absent (line 10) | @SuppressWarnings("unchecked")  // Type erasure has us covered here, w...
    method fromNullable (line 15) | public static <T> Optional<T> fromNullable(T data) {
    method of (line 22) | public static <T> Optional<T> of(T data) {
    method Optional (line 29) | private Optional(T data) {
    method isPresent (line 33) | public boolean isPresent() {
    method isAbsent (line 37) | public boolean isAbsent() {
    method get (line 41) | public T get() {
    method or (line 48) | public T or(T elseCase) {
    method or (line 52) | public Optional<T> or(Optional<T> elseCase) {
    method or (line 56) | public Optional<T> or(Func0<Optional<T>> elseFunc) {
    type Func0 (line 60) | public interface Func0<V> {
      method call (line 62) | V call();
    method equals (line 65) | @Override
    method hashCode (line 79) | @Override
    method toString (line 84) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/AudioTracks.java
  class AudioTracks (line 7) | public final class AudioTracks implements Iterable<PlayerAudioTrack> {
    method from (line 11) | public static AudioTracks from(List<PlayerAudioTrack> audioTracks) {
    method AudioTracks (line 15) | private AudioTracks(List<PlayerAudioTrack> playerAudioTracks) {
    method get (line 19) | public PlayerAudioTrack get(int index) {
    method size (line 23) | public int size() {
    method iterator (line 27) | @Override
    method equals (line 32) | @Override
    method hashCode (line 46) | @Override
    method toString (line 51) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/Bitrate.java
  class Bitrate (line 3) | public final class Bitrate {
    method fromBitsPerSecond (line 9) | public static Bitrate fromBitsPerSecond(long bitsPerSecond) {
    method Bitrate (line 13) | private Bitrate(long bitsPerSecond) {
    method asKilobits (line 17) | public long asKilobits() {
    method equals (line 21) | @Override
    method hashCode (line 35) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/Either.java
  class Either (line 3) | public abstract class Either<L, R> {
    method left (line 5) | public static <L, R> Either<L, R> left(L left) {
    method right (line 9) | public static <L, R> Either<L, R> right(R right) {
    method Either (line 13) | Either() {
    method apply (line 17) | public abstract void apply(Consumer<L> leftConsumer, Consumer<R> right...
    class Left (line 19) | static class Left<L, R> extends Either<L, R> {
      method Left (line 23) | Left(L valueLeft) {
      method apply (line 27) | @Override
    class Right (line 33) | static class Right<L, R> extends Either<L, R> {
      method Right (line 37) | Right(R valueRight) {
      method apply (line 41) | @Override
    type Consumer (line 47) | public interface Consumer<T> {
      method accept (line 48) | void accept(T value);

FILE: core/src/main/java/com/novoda/noplayer/model/KeySetId.java
  class KeySetId (line 5) | public final class KeySetId {
    method of (line 9) | public static KeySetId of(byte[] sessionId) {
    method KeySetId (line 13) | @SuppressWarnings("PMD.ArrayIsStoredDirectly")  // This array can only...
    method asBytes (line 18) | public byte[] asBytes() {
    method equals (line 22) | @Override
    method hashCode (line 36) | @Override
    method toString (line 41) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/LoadTimeout.java
  class LoadTimeout (line 8) | public class LoadTimeout {
    method LoadTimeout (line 19) | public LoadTimeout(Clock clock, Handler handler) {
    method start (line 24) | public void start(Timeout timeout, NoPlayer.LoadTimeoutCallback loadTi...
    method run (line 33) | @Override
    method cancel (line 44) | public void cancel() {

FILE: core/src/main/java/com/novoda/noplayer/model/NoPlayerCue.java
  class NoPlayerCue (line 6) | public class NoPlayerCue {
    method NoPlayerCue (line 21) | @SuppressWarnings({"checkstyle:ParameterNumber", "PMD.ExcessiveParamet...
    method text (line 48) | public CharSequence text() {
    method textAlignment (line 52) | public Alignment textAlignment() {
    method bitmap (line 56) | public Bitmap bitmap() {
    method line (line 60) | public float line() {
    method lineType (line 64) | public int lineType() {
    method lineAnchor (line 68) | public int lineAnchor() {
    method position (line 72) | public float position() {
    method positionAnchor (line 76) | public int positionAnchor() {
    method size (line 80) | public float size() {
    method bitmapHeight (line 84) | public float bitmapHeight() {
    method windowColorSet (line 88) | public boolean windowColorSet() {
    method windowColor (line 92) | public int windowColor() {
    method toString (line 96) | @Override
    method equals (line 114) | @Override
    method hashCode (line 161) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/PlayerAudioTrack.java
  class PlayerAudioTrack (line 5) | public class PlayerAudioTrack {
    method PlayerAudioTrack (line 16) | @SuppressWarnings("checkstyle:ParameterNumber") // TODO group paramete...
    method groupIndex (line 35) | public int groupIndex() {
    method formatIndex (line 39) | public int formatIndex() {
    method trackId (line 43) | public String trackId() {
    method language (line 47) | public String language() {
    method mimeType (line 51) | public String mimeType() {
    method numberOfChannels (line 55) | public int numberOfChannels() {
    method frequency (line 59) | public int frequency() {
    method audioTrackType (line 63) | public AudioTrackType audioTrackType() {
    method equals (line 67) | @Override
    method hashCode (line 102) | @Override
    method toString (line 115) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/PlayerSubtitleTrack.java
  class PlayerSubtitleTrack (line 3) | public class PlayerSubtitleTrack {
    method PlayerSubtitleTrack (line 13) | public PlayerSubtitleTrack(int groupIndex,
    method groupIndex (line 29) | public int groupIndex() {
    method formatIndex (line 33) | public int formatIndex() {
    method trackId (line 37) | public String trackId() {
    method language (line 41) | public String language() {
    method mimeType (line 45) | public String mimeType() {
    method numberOfChannels (line 49) | public int numberOfChannels() {
    method frequency (line 53) | public int frequency() {
    method equals (line 57) | @Override
    method hashCode (line 89) | @Override
    method toString (line 101) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/PlayerVideoTrack.java
  class PlayerVideoTrack (line 5) | public class PlayerVideoTrack {
    method PlayerVideoTrack (line 16) | @SuppressWarnings("checkstyle:ParameterNumber") // TODO group paramete...
    method groupIndex (line 28) | public int groupIndex() {
    method formatIndex (line 32) | public int formatIndex() {
    method id (line 36) | public String id() {
    method contentType (line 40) | public ContentType contentType() {
    method width (line 44) | public int width() {
    method height (line 48) | public int height() {
    method fps (line 52) | public int fps() {
    method bitrate (line 56) | public int bitrate() {
    method equals (line 60) | @Override
    method hashCode (line 95) | @Override
    method toString (line 108) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/TextCues.java
  class TextCues (line 6) | public final class TextCues {
    method of (line 10) | public static TextCues of(List<NoPlayerCue> cues) {
    method TextCues (line 14) | private TextCues(List<NoPlayerCue> cues) {
    method size (line 18) | public int size() {
    method isEmpty (line 22) | public boolean isEmpty() {
    method get (line 26) | public NoPlayerCue get(int position) {
    method equals (line 30) | @Override
    method hashCode (line 44) | @Override
    method toString (line 49) | @Override

FILE: core/src/main/java/com/novoda/noplayer/model/Timeout.java
  class Timeout (line 5) | public final class Timeout {
    method fromSeconds (line 11) | public static Timeout fromSeconds(long timeoutInSeconds) {
    method Timeout (line 15) | private Timeout(long timeoutInMillis) {
    method inMillis (line 19) | public long inMillis() {

FILE: core/src/main/java/com/novoda/noplayer/text/NoPlayerSubtitleDecoderFactory.java
  class NoPlayerSubtitleDecoderFactory (line 19) | @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.StdCyclomaticComplex...
    method supportsFormat (line 22) | @Override
    method createDecoder (line 38) | @Override

FILE: core/src/test/java/com/google/android/exoplayer2/ExoPlaybackExceptionFactory.java
  class ExoPlaybackExceptionFactory (line 3) | public class ExoPlaybackExceptionFactory {
    method createForUnexpected (line 5) | public static ExoPlaybackException createForUnexpected(RuntimeExceptio...

FILE: core/src/test/java/com/google/android/exoplayer2/drm/FrameworkMediaCryptoFixture.java
  class FrameworkMediaCryptoFixture (line 8) | public final class FrameworkMediaCryptoFixture {
    method FrameworkMediaCryptoFixture (line 13) | private FrameworkMediaCryptoFixture() throws MediaCryptoException {
    method aFrameworkMediaCrypto (line 17) | public static FrameworkMediaCryptoFixture aFrameworkMediaCrypto() thro...
    method withMediaCrypto (line 21) | public FrameworkMediaCryptoFixture withMediaCrypto(MediaCrypto mediaCr...
    method withForceAllowInsecureDecoderComponents (line 26) | public FrameworkMediaCryptoFixture withForceAllowInsecureDecoderCompon...
    method build (line 31) | public FrameworkMediaCrypto build() {

FILE: core/src/test/java/com/novoda/noplayer/LoadTimeoutTest.java
  class LoadTimeoutTest (line 24) | public class LoadTimeoutTest {
    method setUp (line 46) | @Before
    method givenTimeoutIsReached_whenStarting_thenOnLoadTimeoutIsCalled (line 59) | @Test
    method givenTimeoutIsNotReached_whenStarting_thenTimeoutIsRescheduled (line 68) | @Test

FILE: core/src/test/java/com/novoda/noplayer/NoPlayerCreatorTest.java
  class NoPlayerCreatorTest (line 32) | @RunWith(Enclosed.class)
    class Base (line 35) | public abstract static class Base {
      method setUp (line 61) | @Before
      method prioritizedPlayerTypes (line 69) | abstract List<PlayerType> prioritizedPlayerTypes();
    class GivenMediaPlayerPrioritized (line 72) | public static class GivenMediaPlayerPrioritized extends Base {
      method prioritizedPlayerTypes (line 74) | @Override
      method whenCreatingPlayerWithDrmTypeNone_thenReturnsMediaPlayer (line 79) | @Test
      method whenCreatingPlayerWithDrmTypeWidevineClassic_thenReturnsMediaPlayer (line 86) | @Test
      method whenCreatingPlayerWithDrmTypeWidevineModularStream_thenReturnsExoPlayer (line 93) | @Test
      method whenCreatingPlayerWithDrmTypeWidevineModularDownload_thenReturnsExoPlayer (line 100) | @Test
    class GivenExoPlayerPlayerPrioritized (line 108) | public static class GivenExoPlayerPlayerPrioritized extends Base {
      method prioritizedPlayerTypes (line 110) | @Override
      method whenCreatingPlayerWithDrmTypeNone_thenReturnsExoPlayer (line 115) | @Test
      method whenCreatingPlayerWithDrmTypeWidevineClassic_thenReturnsMediaPlayer (line 122) | @Test
      method whenCreatingPlayerWithDrmTypeWidevineModularStream_thenReturnsExoPlayer (line 129) | @Test
      method whenCreatingPlayerWithDrmTypeWidevineModularDownload_thenReturnsExoPlayer (line 136) | @Test

FILE: core/src/test/java/com/novoda/noplayer/PlayerSurfaceHolderTest.java
  class PlayerSurfaceHolderTest (line 20) | @RunWith(MockitoJUnitRunner.class)
    method setUp (line 35) | @Before
    method whenCreatingPlayerSurfaceHolderWithSurfaceView_thenAttachCallbackToSurfaceHolder (line 41) | @Test
    method whenCreatingPlayerSurfaceHolderWithTextureView_thenAttachSurfaceTextureListenerToTextureView (line 49) | @Test
    method givenPlayerSurfaceHolderContainsSurfaceView_whenAttachingVideoPlayer_thenSetsVideoSurfaceView (line 57) | @Test
    method givenPlayerSurfaceHolderContainsTextureView_whenAttachingVideoPlayer_thenSetsVideoTextureView (line 66) | @Test
    method givenPlayerSurfaceHolderContainsNoView_whenAttachingVideoPlayer_thenThrowsException (line 75) | @Test

FILE: core/src/test/java/com/novoda/noplayer/PlayerTypeTest.java
  class PlayerTypeTest (line 7) | public class PlayerTypeTest {
    method givenUnknownPlayerType_thenThrows (line 12) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/HeartTest.java
  class HeartTest (line 19) | public class HeartTest {
    method setUp (line 27) | @Before
    method throwsException_whenStartingHeartWithoutBindingAction (line 41) | @Test(expected = IllegalStateException.class)
    method throwsException_whenForcingBeatWithoutBindingAction (line 46) | @Test(expected = IllegalStateException.class)
    method removesCallbacks_whenStartingHeart (line 51) | @Test
    method schedulesNextBeat_whenStartingHeart (line 61) | @Test
    method doesNotEmitOnBeat_whenPlayerIsNotPlaying (line 71) | @Test
    method emitsOnBeat_whenPlayerIsPlaying (line 81) | @Test
    method emitsOnBeat_whenForcingBeat (line 92) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/drm/provision/HttpPostingProvisionExecutorTest.java
  class HttpPostingProvisionExecutorTest (line 19) | public class HttpPostingProvisionExecutorTest {
    method setUp (line 34) | @Before
    method givenNonCapableProvisionCapabilities_whenProvisioning_thenAnUnableToProvisionExceptionIsThrown (line 39) | @Test(expected = UnableToProvisionException.class)
    method givenCapableProvisionCapabilities_whenProvisioning_thenTheRequestUrlIsExpected (line 47) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/drm/provision/ProvisioningCapabilitiesFixtures.java
  class ProvisioningCapabilitiesFixtures (line 5) | public final class ProvisioningCapabilitiesFixtures {
    method aProvisioningCapabilities (line 7) | public static ProvisioningCapabilitiesFixtures aProvisioningCapabiliti...
    method ProvisioningCapabilitiesFixtures (line 11) | private ProvisioningCapabilitiesFixtures() {
    method thatCanProvision (line 15) | public ProvisioningCapabilities thatCanProvision() {
    method thatCannotProvision (line 19) | public ProvisioningCapabilities thatCannotProvision() {

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerFacadeTest.java
  class ExoPlayerFacadeTest (line 61) | @RunWith(Enclosed.class)
    class GivenVideoNotLoaded (line 83) | public static class GivenVideoNotLoaded extends Base {
      method whenResetting_thenReleasesUnderlyingPlayer (line 92) | @Test
      method whenLoadingVideo_thenAddsPlayerEventListener (line 100) | @Test
      method whenLoadingVideo_thenSetsAnalyticsListener (line 108) | @Test
      method givenSurfaceContainerContainsSurfaceView_whenLoadingVideo_thenSetsSurfaceViewOnExoPlayer (line 116) | @Test
      method givenSurfaceContainerContainsTextureView_whenLoadingVideo_thenSetsTextureViewOnExoPlayer (line 124) | @Test
      method givenLollipopDevice_whenLoadingVideo_thenSetsMovieAudioAttributesOnExoPlayer (line 132) | @Test
      method givenNonLollipopDevice_whenLoadingVideo_thenDoesNotSetAudioAttributesOnExoPlayer (line 144) | @Test
      method givenMediaSource_whenLoadingVideo_thenPreparesInternalExoPlayer (line 153) | @Test
      method givenInitialPosition_whenLoadingVideo_thenPerformsSeekBeforePreparing (line 162) | @Test
      method givenNoInitialPosition_whenLoadingVideo_thenDoesNotPerformSeekBeforePreparing (line 176) | @Test
      method whenQueryingIsPlaying_thenReturnsFalse (line 187) | @Test
      method whenQueryingPlayheadPosition_thenThrowsIllegalStateException (line 195) | @Test
      method whenQueryingMediaDuration_thenThrowsIllegalStateException (line 202) | @Test
      method whenQueryingBufferPercentage_thenThrowsIllegalStateException (line 209) | @Test
      method whenPausing_thenThrowsIllegalStateException (line 216) | @Test
      method whenSeeking_thenThrowsIllegalStateException (line 223) | @Test
      method whenSelectingAudioTrack_thenThrowsIllegalStateException (line 230) | @Test
      method whenGettingAudioTracks_thenThrowsIllegalStateException (line 239) | @Test
      method selectSubtitleTrack_thenThrowsIllegalStateException (line 248) | @Test
      method whenSetVolume_thenThrowsIllegalStateException (line 257) | @Test
      method whenGetVolume_thenThrowsIllegalStateException (line 264) | @Test
    class GivenVideoIsLoaded (line 272) | public static class GivenVideoIsLoaded extends Base {
      method setUp (line 279) | @Override
      method givenPlayerIsLoaded (line 285) | private void givenPlayerIsLoaded() {
      method whenResetting_thenReleasesUnderlyingPlayer (line 290) | @Test
      method whenPausing_thenSetsPlayWhenReadyToFalse (line 297) | @Test
      method whenSeeking_thenSeeksToPosition (line 305) | @Test
      method whenStartingPlay_thenSetsPlayWhenReadyToTrue (line 314) | @Test
      method whenStartingPlayAtVideoPosition_thenSeeksToPosition (line 322) | @Test
      method whenStartingPlayAtVideoPosition_thenSetsPlayWhenReadyToTrue (line 329) | @Test
      method givenExoPlayerIsReadyToPlay_whenQueryingIsPlaying_thenReturnsTrue (line 336) | @Test
      method whenGettingPlayheadPosition_thenReturnsCurrentPosition (line 345) | @Test
      method whenGettingMediaDuration_thenReturnsDuration (line 354) | @Test
      method whenGettingBufferPercentage_thenReturnsBufferPercentage (line 363) | @Test
      method whenSelectingAudioTrack_thenDelegatesToTrackSelector (line 372) | @Test
      method givenSelectingAudioTrackSuceeds_whenSelectingAudioTrack_thenReturnsTrue (line 381) | @Test
      method givenSelectingAudioTrackFails_whenSelectingAudioTrack_thenReturnsFalse (line 391) | @Test
      method whenSelectingSubtitlesTrack_thenDelegatesToTrackSelector (line 401) | @Test
      method givenSelectingTextTrackSuceeds_whenSelectingSubtitlesTrack_thenReturnsTrue (line 410) | @Test
      method givenSelectingTextTrackFails_whenSelectingSubtitlesTrack_thenReturnsFalse (line 420) | @Test
      method whenGettingAudioTracks_thenDelegatesToTrackSelector (line 430) | @Test
      method whenGettingSelectedVideoTrack_thenDelegatesTrackSelector (line 439) | @Test
      method whenSelectingVideoTrack_thenDelegatesToTrackSelector (line 448) | @Test
      method whenGettingVideoTracks_thenDelegatesToTrackSelector (line 457) | @Test
      method whenSetRepeatingTrue_thenSetsRepeatModeAll (line 466) | @Test
      method whenSetRepeatingFalse_thenSetsRepeatModeOff (line 473) | @Test
      method whenSetVolume_thenSetsPlayerVolume (line 480) | @Test
      method whenGetVolume_thenGetsPlayerVolume (line 487) | @Test
    class Base (line 498) | public abstract static class Base {
      method setUp (line 544) | @Before
      method givenMediaSource (line 567) | MediaSource givenMediaSource(Options options) {

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerInformationTest.java
  class ExoPlayerInformationTest (line 11) | public class ExoPlayerInformationTest {
    method setUp (line 15) | @Before
    method whenReadingName_thenReturnsExoPlayer (line 20) | @Test
    method whenReadingVersion_thenReturnsExoPlayerLibraryVersion (line 28) | @Test
    method whenPlayerType_thenReturnsExoPlayer (line 36) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerTwoImplTest.java
  class ExoPlayerTwoImplTest (line 51) | @RunWith(Enclosed.class)
    method onLoadTimeout (line 68) | @Override
    class GivenVideoNotLoaded (line 75) | public static class GivenVideoNotLoaded extends Base {
      method whenInitialisingPlayer_thenBindsListenersToForwarder (line 80) | @Test
      method whenInitialisingPlayer_thenBindsHeart (line 93) | @Test
      method givenPlayerIsInitialised_whenVideoIsPrepared_thenCancelsTimeout (line 101) | @Test
      method givenPlayerIsInitialised_whenVideoHasError_thenPlayerResourcesAreReleased_andNotListeners (line 114) | @Test
      method givenPlayerIsInitialised_andPlayerViewIsAttached_whenVideoSizeChanges_thenPlayerVideoWidthAndHeightMatches (line 132) | @Test
      method givenPlayerIsInitialised_whenAttachingPlayerView_thenAddsPlayerViewVideoSizeChangedListenerToListenersHolder (line 150) | @Test
      method whenStopping_thenPlayerResourcesAreReleased (line 162) | @Test
      method whenReleasing_thenPlayerResourcesAreReleased (line 174) | @Test
      method givenAttachedPlayerView_whenStopping_thenPlayerResourcesAreReleased (line 187) | @Test
      method givenAttachedPlayerView_whenReleasing_thenPlayerResourcesAreReleased (line 201) | @Test
      method whenLoadingVideo_thenDelegatesLoadingToFacade (line 216) | @Test
      method whenLoadingVideoWithTimeout_thenDelegatesLoadingToFacade (line 225) | @Test
      method whenLoadingVideoWithTimeout_thenStartsLoadTimeout (line 234) | @Test
      method whenGettingPlayerInformation_thenReturnsPlayerInformation (line 243) | @Test
      method whenQueryingIsPlaying_thenReturnsFalse (line 251) | @Test
      method whenAttachingPlayerView_thenAddsVideoSizeChangedListener (line 259) | @Test
      method whenAttachingPlayerView_thenAddsStateChangedListener (line 267) | @Test
      method givenAttachedPlayerView_whenDetachingPlayerView_thenRemovesVideoSizeChangedListener (line 275) | @Test
      method givenAttachedPlayerView_whenDetachingPlayerView_thenRemovesStateChangedListener (line 284) | @Test
      method givenAttachedPlayerView_whenLoadingVideo_thenMakesContainerVisible (line 293) | @Test
      method givenPlayerHasPlayedVideo_whenLoadingVideo_thenPlayerIsReleased_andNotListeners (line 302) | @Test
      method givenPlayerHasPlayedVideo_whenLoadingVideoWithTimeout_thenPlayerResourcesAreReleased_andNotListeners (line 316) | @Test
      method givenPlayerHasNotPlayedVideo_whenLoadingVideo_thenPlayerResourcesAreNotReleased (line 330) | @Test
      method givenPlayerHasNotPlayedVideo_whenLoadingVideoWithTimeout_thenPlayerResourcesAreNotReleased (line 343) | @Test
    class GivenAttachedAndVideoIsLoaded (line 357) | public static class GivenAttachedAndVideoIsLoaded extends Base {
      method setUp (line 361) | @Override
      method whenLoadingVideo_thenAddsStateChangedListenerToListenersHolder (line 368) | @Test
      method whenLoadingVideo_thenAddsVideoSizeChangedListenerToListenersHolder (line 376) | @Test
      method whenReleasing_thenResetsFacade (line 384) | @Test
      method whenStartingPlayback_thenStartsBeatingHeart (line 391) | @Test
      method whenPausing_thenNotifiesStateListenersThatVideoIsPaused (line 399) | @Test
      method givenHeartIsBeating_whenPausing_thenStopsBeatingHeart (line 406) | @Test
      method givenHeartIsBeating_whenPausing_thenForcesHeartBeat (line 415) | @Test
      method givenHeartIsNotBeating_whenPausing_thenDoesNotStopBeatingHeart (line 424) | @Test
      method givenHeartIsNotBeating_whenPausing_thenDoesNotForceHeartBeat (line 433) | @Test
      method whenSeeking_thenSeeksToPosition (line 442) | @Test
      method whenStartingPlayback_andSurfaceHolderIsReady_thenPlaysFacadeWithSurfaceHolder (line 451) | @Test
      method whenStartingPlayAtVideoPosition_thenSeeksToPosition (line 458) | @Test
      method whenStartingPlayAtVideoPosition_thenStartsBeatingHeart (line 465) | @Test
      method whenStartingPlay_thenNotifiesStateListenersThatVideoIsPlaying (line 472) | @Test
      method whenStartingPlayAtVideoPosition_thenNotifiesStateListenersThatVideoIsPlaying (line 480) | @Test
      method whenSelectingSubtitlesTrack_thenShowsPlayerSubtitlesView (line 487) | @Test
      method givenSelectingSubtitleTrackSuceeds_whenSelectingSubtitlesTrack_thenReturnsTrue (line 496) | @Test
      method givenSelectingSubtitleTrackFails_whenSelectingSubtitlesTrack_thenReturnsFalse (line 506) | @Test
      method givenPlayerHasLoadedSubtitleCues_whenSelectingSubtitlesTrack_thenSetsSubtitleCuesOnView (line 516) | @Test
      method givenPlayerHasLoadedSubtitleCues (line 527) | private TextCues givenPlayerHasLoadedSubtitleCues() {
      method whenClearingSubtitles_thenHidesPlayerSubtitlesView (line 540) | @Test
      method whenSetRepeating_thenSetRepeating (line 547) | @Test
      method whenSetVolume_thenSetVolumeOnExoPlayer (line 554) | @Test
      method whenGetVolume_thenReturnVolumeFromExoPlayer (line 561) | @Test
    class Base (line 571) | public abstract static class Base {
      method setUp (line 619) | @Before

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreatorTest.java
  class NoPlayerExoPlayerCreatorTest (line 17) | public class NoPlayerExoPlayerCreatorTest {
    method setUp (line 36) | @Before
    method whenCreatingExoPlayerTwo_thenInitialisesPlayer (line 42) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/PlayerSubtitleTrackFixture.java
  class PlayerSubtitleTrackFixture (line 5) | class PlayerSubtitleTrackFixture {
    method PlayerSubtitleTrackFixture (line 15) | private PlayerSubtitleTrackFixture() {
    method anInstance (line 19) | static PlayerSubtitleTrackFixture anInstance() {
    method withGroupIndex (line 23) | PlayerSubtitleTrackFixture withGroupIndex(int groupIndex) {
    method withFormatIndex (line 28) | PlayerSubtitleTrackFixture withFormatIndex(int formatIndex) {
    method withTrackId (line 33) | PlayerSubtitleTrackFixture withTrackId(String trackId) {
    method withLanguage (line 38) | PlayerSubtitleTrackFixture withLanguage(String language) {
    method withMimeType (line 43) | PlayerSubtitleTrackFixture withMimeType(String mimeType) {
    method withNumberOfChannels (line 48) | PlayerSubtitleTrackFixture withNumberOfChannels(int numberOfChannels) {
    method withFrequency (line 53) | PlayerSubtitleTrackFixture withFrequency(int frequency) {
    method build (line 58) | PlayerSubtitleTrack build() {

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/SecurityDowngradingCodecSelectorTest.java
  class SecurityDowngradingCodecSelectorTest (line 16) | public class SecurityDowngradingCodecSelectorTest {
    method whenContentIsSecure_thenRequiresSecureDecoderIsFalse (line 29) | @Test
    method whenContentIsInsecure_thenRequiresSecureDecoderIsFalse (line 40) | @Test
    method whenGettingPassthroughDecoderInfo_thenDelegates (line 51) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreatorFactoryTest.java
  class DrmSessionCreatorFactoryTest (line 25) | public class DrmSessionCreatorFactoryTest {
    method setUp (line 47) | @Before
    method givenDrmTypeNone_whenCreatingDrmSessionCreator_thenReturnsNoDrmSession (line 52) | @Test
    method givenDrmTypeWidevineClassic_whenCreatingDrmSessionCreator_thenReturnsNoDrmSession (line 59) | @Test
    method givenDrmTypeWidevineModularStream_whenCreatingDrmSessionCreator_thenReturnsStreaming (line 66) | @Test
    method givenDrmTypeWidevineModularStream_andAndroidVersionDoesNotSupportMediaDrmApis_whenCreatingDrmSessionCreator_thenThrowsUnableToCreatePlayerException (line 73) | @Test
    method givenDrmTypeWidevineModularDownload_whenCreatingDrmSessionCreator_thenReturnsDownload (line 83) | @Test
    method givenDrmTypeWidevineDownloadStream_andAndroidVersionDoesNotSupportMediaDrmApis_whenCreatingDrmSessionCreator_thenThrowsUnableToCreatePlayerException (line 90) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/drm/LocalDrmSessionManagerTest.java
  class LocalDrmSessionManagerTest (line 36) | public class LocalDrmSessionManagerTest {
    method setUp (line 60) | @Before
    method givenDrmDataContainsDrmScheme_whenCheckingCanAcquireSession_thenReturnsTrue (line 74) | @Test
    method givenDrmDataDoesNotContainDrmScheme_whenCheckingCanAcquireSession_thenReturnsFalse (line 86) | @Test
    method givenValidMediaDrm_whenAcquiringSession_thenRestoresKeys (line 98) | @Test
    method givenValidMediaDrm_whenAcquiringSession_thenReturnsLocalDrmSession (line 107) | @Test
    method givenOpeningSessionError_whenAcquiringSession_thenNotifiesErrorEventListenerOnHandler (line 117) | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    method givenOpeningSessionError_whenAcquiringSession_thenReturnsInvalidDrmSession (line 130) | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    method givenAcquiredSession_whenReleasingSession_thenClosesCurrentSession (line 142) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/error/ErrorFormatterTest.java
  class ErrorFormatterTest (line 11) | public class ErrorFormatterTest {
    method givenThrowable_whenFormattingMessage_thenReturnsExpectedMessageFormat (line 15) | @Test
    method givenMediaCodecException_whenFormattingMessage_thenReturnsExpectedMessageFormat (line 24) | @Test
    class IncorrectFormatThrowable (line 37) | private class IncorrectFormatThrowable extends Throwable {
      method IncorrectFormatThrowable (line 39) | IncorrectFormatThrowable(String message) {

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/forwarder/ExoPlayerErrorMapperTest.java
  class ExoPlayerErrorMapperTest (line 91) | @RunWith(Parameterized.class)
    method parameters (line 101) | @Parameterized.Parameters(name = "{0} with detail {1} is mapped from {...
    method createSource (line 157) | private static ExoPlaybackException createSource(IOException exception) {
    method createRenderer (line 161) | private static ExoPlaybackException createRenderer(Exception exception) {
    method mapErrors (line 165) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/mediasource/AudioFormatFixture.java
  class AudioFormatFixture (line 9) | class AudioFormatFixture {
    method anAudioFormat (line 23) | static AudioFormatFixture anAudioFormat() {
    method withId (line 27) | AudioFormatFixture withId(String id) {
    method withSampleMimeType (line 32) | AudioFormatFixture withSampleMimeType(String sampleMimeType) {
    method withCodecs (line 37) | AudioFormatFixture withCodecs(String codecs) {
    method withBitrate (line 42) | AudioFormatFixture withBitrate(int bitrate) {
    method withMaxInputSize (line 47) | AudioFormatFixture withMaxInputSize(int maxInputSize) {
    method withChannelCount (line 52) | AudioFormatFixture withChannelCount(int channelCount) {
    method withSampleRate (line 57) | AudioFormatFixture withSampleRate(int sampleRate) {
    method withInitializationData (line 62) | AudioFormatFixture withInitializationData(List<byte[]> initializationD...
    method withDrmInitData (line 67) | AudioFormatFixture withDrmInitData(DrmInitData drmInitData) {
    method withSelectionFlags (line 72) | AudioFormatFixture withSelectionFlags(int selectionFlags) {
    method withLanguage (line 77) | AudioFormatFixture withLanguage(String language) {
    method build (line 82) | Format build() {

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/mediasource/AudioTrackTypeTest.java
  class AudioTrackTypeTest (line 7) | public class AudioTrackTypeTest {
    method givenSelectionFlagIsZero_whenCreatingAudioTrackType_thenReturnsAlternative (line 13) | @Test
    method givenSelectionFlagIsOne_whenCreatingAudioTrackType_thenReturnsMain (line 20) | @Test
    method givenAnyOtherSelectionFlag_whenCreatingAudioTrackType_thenReturnsUnknown (line 27) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerAudioTrackSelectorTest.java
  class ExoPlayerAudioTrackSelectorTest (line 28) | public class ExoPlayerAudioTrackSelectorTest {
    method setUp (line 55) | @Before
    method givenTrackSelectorContainsTracks_whenSelectingAudioTrack_thenSelectsTrack (line 60) | @Test
    method givenTrackSelectorContainsUnsupportedTracks_whenGettingAudioTracks_thenReturnsOnlySupportedTracks (line 71) | @Test
    method givenTrackSelectorContainsTracks (line 80) | private TrackGroupArray givenTrackSelectorContainsTracks() {
    method whenSelectingAudioTrack (line 94) | private ArgumentCaptor<DefaultTrackSelector.SelectionOverride> whenSel...
    method givenTrackSelectorContainsUnsupportedTracks (line 102) | private void givenTrackSelectorContainsUnsupportedTracks() {
    method expectedSupportedAudioTracks (line 117) | private AudioTracks expectedSupportedAudioTracks() {

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerVideoTrackSelectorTest.java
  class ExoPlayerVideoTrackSelectorTest (line 32) | public class ExoPlayerVideoTrackSelectorTest {
    method setUp (line 76) | @Before
    method givenTrackSelectorContainsTracks_whenSelectingVideoTrack_thenSelectsTrack (line 81) | @Test
    method givenTrackSelector_whenGettingVideoTracks_thenReturnsSupportedTracks (line 92) | @Test
    method givenTrackSelector_whenGettingCurrentlySelectedVideoTrack_thenReturnsSelectedTrack (line 101) | @Test
    method givenNoCurrentlySelectedTrack_whenGettingCurrentlySelectedVideoTrack_thenReturnsAbsent (line 111) | @Test
    method givenTrackSelector_whenClearMaxVideoBitrate_thenClearsMaxVideoBitrate (line 121) | @Test
    method givenTrackSelector_whenSetMaxVideoBitrate1000000_thenSetsMaxVideoBitrate1000000 (line 130) | @Test
    method givenTrackSelectorContainsTracks (line 139) | private void givenTrackSelectorContainsTracks() {
    method whenSelectingVideoTrack (line 146) | private ArgumentCaptor<DefaultTrackSelector.SelectionOverride> whenSel...

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/mediasource/RendererTrackIndexExtractorTest.java
  class RendererTrackIndexExtractorTest (line 15) | public class RendererTrackIndexExtractorTest {
    method setUp (line 22) | @Before
    method givenAudioTrackAtPositionZero_whenExtractingAudioIndex_thenReturnsIndexZero (line 27) | @Test
    method givenVideoTrackAtPositionZero_whenExtractingVideoIndex_thenReturnsIndexZero (line 35) | @Test
    method givenSubtitlesTrackAtPositionZero_whenExtractingTextIndex_thenReturnsIndexZero (line 43) | @Test
    method givenThreeTrackTypes_whenExtractingAudioIndexes_thenReturnsIndexOne (line 51) | @Test
    method givenNoAudioTrack_whenExtractingAudioIndex_thenReturnsEmpty (line 59) | @Test
    method givenNoVideoTrack_whenExtractingVideoIndex_thenReturnsEmpty (line 66) | @Test
    method givenNoTextTrack_whenExtractingTextIndex_thenReturnsEmpty (line 73) | @Test
    method getRendererTypeFor (line 81) | @Override
    method getRendererTypeFor (line 92) | @Override
    method getRendererTypeFor (line 103) | @Override
    method getRendererTypeFor (line 114) | @Override
    method getRendererTypeFor (line 121) | @Override

FILE: core/src/test/java/com/novoda/noplayer/internal/exoplayer/mediasource/VideoFormatFixture.java
  class VideoFormatFixture (line 9) | public class VideoFormatFixture {
    method aVideoFormat (line 22) | public static VideoFormatFixture aVideoFormat() {
    method VideoFormatFixture (line 26) | private VideoFormatFixture() {
    method withId (line 30) | public VideoFormatFixture withId(String id) {
    method withSampleMimeType (line 35) | public VideoFormatFixture withSampleMimeType(String sampleMimeType) {
    method withCodecs (line 40) | public VideoFormatFixture withCodecs(String codecs) {
    method withBitrate (line 45) | public VideoFormatFixture withBitrate(int bitrate) {
    method withMaxInputSize (line 50) | public VideoFormatFixture withMaxInputSize(int maxInputSize) {
    method withWidth (line 55) | public VideoFormatFixture withWidth(int width) {
    method withHeight (line 60) | public VideoFormatFixture withHeight(int height) {
    method withFrameRate (line 65) | public VideoFormatFixture withFrameRate(float frameRate) {
    method withInitializationData (line 70) | public VideoFormatFixture withInitializationData(List<byte[]> initiali...
    method withDrmInitData (line 75) | public VideoFormatFixture withDrmInitData(DrmInitData drmInitData) {
    method build (line 80) | public Format build() {

FILE: core/src/test/java/com/novoda/noplayer/internal/listeners/BufferStateListenersTest.java
  class BufferStateListenersTest (line 14) | public class BufferStateListenersTest {
    method setUp (line 27) | @Before
    method givenBufferStateListeners_whenNotifyingOfBufferStarted_thenAllTheListenersAreNotifiedAppropriately (line 34) | @Test
    method givenBufferStateListeners_whenNotifyingOfBufferCompleted_thenAllTheListenersAreNotifiedAppropriately (line 43) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/listeners/CompletionListenersTest.java
  class CompletionListenersTest (line 16) | public class CompletionListenersTest {
    method setUp (line 26) | @Before
    method whenCallingOnCompletion_thenNotifiesOnCompletion (line 32) | @Test
    method whenCallingOnCompletionTwice_thenDoesNothing (line 39) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/listeners/StateChangedListenersTest.java
  class StateChangedListenersTest (line 15) | public class StateChangedListenersTest {
    method setUp (line 25) | @Before
    method whenDoubleCallingOnVideoPlaying_thenEmitsOnlyFirstOnVideoPlayingEvent (line 32) | @Test
    method whenDoubleCallingOnVideoPaused_thenEmitsOnlyFirstOnVideoPausedEvent (line 40) | @Test
    method whenDoubleCallingOnVideoStopped_thenEmitsOnlyFirstOnVideoStoppedEvent (line 48) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerAudioTrackSelectorTest.java
  class AndroidMediaPlayerAudioTrackSelectorTest (line 28) | public class AndroidMediaPlayerAudioTrackSelectorTest {
    method setUp (line 57) | @Before
    method givenNullMediaPlayer_whenGettingAudioTracks_thenThrowsIllegalState (line 62) | @Test
    method givenTrackSelectorContainsUnsupportedTracks_whenGettingAudioTracks_thenReturnsOnlySupportedTracks (line 69) | @Test
    method givenNullMediaPlayer_whenSelectingAudioTrack_thenThrowsIllegalState (line 78) | @Test
    method whenSelectingAudioTrack_thenMediaPlayerSelectsAudioTrack (line 85) | @Test
    method givenTrackSelectorContainsUnsupportedTracks (line 95) | private void givenTrackSelectorContainsUnsupportedTracks() {
    method expectedAudioTrack (line 113) | private AudioTracks expectedAudioTrack() {

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerFacadeTest.java
  class AndroidMediaPlayerFacadeTest (line 44) | public class AndroidMediaPlayerFacadeTest {
    method setUp (line 103) | @Before
    method givenSurfaceRequesterReturns (line 120) | private void givenSurfaceRequesterReturns(final Either<Surface, Surfac...
    method whenPreparing_thenRequestsAudioFocus (line 131) | @Test
    method whenPreparing_thenDoesNotReleaseMediaPlayer (line 138) | @Test
    method whenPreparingMultipleTimes_thenReleasesMediaPlayer (line 146) | @Test
    method whenPreparing_thenSetsDataSource (line 155) | @Test
    method givenSurfaceRequesterReturnsSurface_whenPreparing_thenSetsSurface (line 162) | @Test
    method givenSurfaceRequesterReturnsSurfaceHolder_whenPreparing_thenSetsDisplay (line 173) | @Test
    method whenPreparing_thenSetsStreamMusicAudioStreamType (line 184) | @Test
    method whenPreparing_thenSetsScreenOnWhilePlayerToTrue (line 191) | @Test
    method whenPreparing_thenPreparesMediaPlayerAsynchronously (line 198) | @Test
    method givenExceptionPreparingMediaPlayer_whenPreparingMediaPlayer_thenForwardsOnError (line 205) | @Test
    method givenBoundPreparedListener_andMediaPlayerIsPrepared_whenPrepared_thenForwardsOnPrepared (line 220) | @Test
    method givenNoBoundPreparedListener_andMediaPlayerIsPrepared_whenPrepared_thenThrowsIllegalStateException (line 230) | @Test
    method givenBoundVideoSizeChangedListener_andMediaPlayerOnPrepared_whenVideoSizeChanges_thenForwardsSizeChanges (line 237) | @Test
    method givenNoBoundVideoSizeChangedListener_andMediaPlayerIsPrepared_whenVideoSizeChanges_thenThrowsIllegalStateException (line 246) | @Test
    method givenBoundCompletionListener_andMediaPlayerIsPrepared_whenCompleted_thenForwardsCompleted (line 255) | @Test
    method givenNoBoundCompletionListener_andMediaPlayerIsPrepared_whenCompleted_thenThrowsIllegalStateException (line 264) | @Test
    method givenBoundErrorListener_andMediaPlayerIsPrepared_whenErroring_thenForwardsError (line 273) | @Test
    method givenNoBoundErrorListener_andMediaPlayerIsPrepared_whenErroring_thenThrowsIllegalStateException (line 282) | @Test
    method givenBoundBufferListener_andMediaPlayerIsPrepared_whenBuffering_thenBufferPercentageIsUpdated (line 291) | @Test
    method givenMediaPlayerIsPrepared_whenReleasing_thenReleasesMediaPlayer (line 303) | @Test
    method givenMediaPlayerIsPreparedWithSurface_whenStarting_thenSetsSurface (line 313) | @Test
    method givenMediaPlayerIsPreparedWithSurfaceHolder_whenStarting_thenSetsDisplay (line 323) | @Test
    method givenMediaPlayerIsNotPrepared_whenStarting_thenThrowsIllegalStateException (line 335) | @Test
    method givenMediaPlayerIsPrepared_whenStarting_thenStartsMediaPlayer (line 342) | @Test
    method givenMediaPlayerIsPlaying_whenPausing_thenPausesMediaPlayer (line 351) | @Test
    method givenMediaPlayerIsNotPlaying_whenPausing_thenDoesNotPausesMediaPlayer (line 362) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenPausing_thenThrowsIllegalStateException (line 373) | @Test
    method givenMediaPlayerIsInPlaybackState_whenGettingDuration_thenReturnsDuration (line 385) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenGettingDuration_thenThrowsIllegalStateException (line 397) | @Test
    method givenMediaPlayerIsInPlaybackState_whenGettingPosition_thenReturnsPosition (line 407) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenGettingPosition_thenThrowsIllegalStateException (line 419) | @Test
    method givenMediaPlayerIsInPlaybackState_whenSeeking_thenSeeksToPosition (line 429) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenSeeking_thenThrowsIllegalStateException (line 440) | @Test
    method whenCheckingIsPlaying_thenDelegatesToPlaystateChecker (line 450) | @Test
    method givenNoMediaPlayer_whenGettingBufferPercentage_thenThrowsIllegalStateException (line 461) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenGettingAudioTracks_thenThrowsIllegalStateException (line 468) | @Test
    method whenGettingAudioTracks_thenDelegatesToTrackSelector (line 478) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenSelectingAudioTracks_thenThrowsIllegalStateException (line 488) | @Test
    method whenSelectingAudioTrack_thenDelegatesToTrackSelector (line 498) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenSelectingSubtitleTrack_thenThrowsIllegalStateException (line 508) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenClearingSubtitleTrack_thenThrowsIllegalStateException (line 518) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenGettingSubtitleTracks_thenThrowsIllegalStateException (line 528) | @Test
    method whenGettingSubtitleTracks_thenReturnsEmptyList (line 538) | @Test
    method whenSelectingSubtitleTrack_thenReturnsFalse (line 547) | @Test
    method whenSettingOnSeekCompleteListener_thenSetsOnSeekCompleteListener (line 556) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenSettingOnSeekCompleteListener_thenThrowsIllegalStateException (line 566) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenGettingVideoTracks_thenThrowsIllegalStateException (line 576) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenGettingSelectedVideoTrack_thenThrowsIllegalStateException (line 586) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenSettingVolume_thenThrowsIllegalStateException (line 596) | @Test
    method whenSettingVolume_thenSetsLeftAndRightVolumeScalars (line 603) | @Test
    method givenMediaPlayerIsNotInPlaybackState_whenGettingVolume_thenThrowsIllegalStateException (line 612) | @Test
    method givenNoVolumeWasSet_whenGettingVolume_theReturnsOne (line 619) | @Test
    method givenVolumeWasSet_whenGettingVolume_theReturnsSetVolume (line 628) | @Test
    method givenMediaPlayerIsPrepared (line 638) | private void givenMediaPlayerIsPrepared() {
    method givenMediaPlayerIsPreparedWith (line 642) | private void givenMediaPlayerIsPreparedWith(Either<Surface, SurfaceHol...
    method whenVideoSizeChanges (line 649) | private void whenVideoSizeChanges() {
    method whenCompleted (line 655) | private void whenCompleted() {
    method whenErroring (line 661) | private void whenErroring() {

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerImplTest.java
  class AndroidMediaPlayerImplTest (line 52) | @RunWith(Enclosed.class)
    class GivenPlayer (line 55) | public static class GivenPlayer extends Base {
      method whenInitialising_thenBindsListenersToForwarder (line 69) | @Test
      method whenInitialising_thenBindsListenerToBufferHeartbeatCallback (line 80) | @Test
      method whenInitialising_thenBindsHeartbeatCallbackToListenerHolder (line 87) | @Test
      method givenInitialised_whenCallingOnPrepared_thenCancelsTimeout (line 94) | @Test
      method whenInitialising_thenBindsHeart (line 107) | @Test
      method givenInitialised_whenCallingOnPrepared_thenSetsOnSeekCompleteListener (line 114) | @Test
      method givenInitialised_whenCallingOnError_thenCancelsTimeout (line 126) | @Test
      method givenInitialised_whenCallingOnError_thenPlayerResourcesAreReleased_andNotListeners (line 138) | @Test
      method givenInitialised_whenCallingOnVideoSizeChanged_thenVideoWidthAndHeightMatches (line 156) | @Test
      method givenAndroidMediaPlayerIsPlaying_whenCallingIsPlaying_thenReturnsTrue (line 169) | @Test
      method whenSeeking_thenSeeksToPosition (line 178) | @Test
      method whenPausing_thenPausesMediaPlayer (line 187) | @Test
      method givenHeartIsBeating_whenPausing_thenStopsBeatingHeart (line 194) | @Test
      method givenHeartIsBeating_whenPausing_thenForcesHeartBeat (line 203) | @Test
      method givenHeartIsNotBeating_whenPausing_thenDoesNotStopBeatingHeart (line 212) | @Test
      method givenHeartIsNotBeating_whenPausing_thenDoesNotForceHeartBeat (line 221) | @Test
      method whenPausing_thenNotifiesStateChangedListenersThatVideoIsPaused (line 230) | @Test
      method givenPlayerIsNotSeeking_whenGettingPlayheadPosition_thenReturnsCurrentMediaPlayerPosition (line 237) | @Test
      method givenPlayerIsSeeking_whenGettingPlayheadPosition_thenReturnsSeekPosition (line 245) | @Test
      method whenGettingMediaDuration_thenReturnsMediaPlayerDuration (line 255) | @Test
      method whenGettingBufferPercentage_thenReturnsMediaPlayerBufferPercentage (line 263) | @Test
      method whenGettingPlayerInformation_thenReturnsMediaPlayerInformation (line 273) | @Test
      method whenAttachingPlayerView_thenAddsVideoSizeChangedListener (line 280) | @Test
      method whenAttachingPlayerView_thenAddsStateChangedListener (line 289) | @Test
      method whenAttachingPlayerView_thenPreventsVideoDriverBug (line 298) | @Test
      method whenDetachingPlayerView_thenRemovesVideoSizeChangedListener (line 305) | @Test
      method whenDetachingPlayerView_thenRemovesStateChangedListener (line 315) | @Test
      method whenDetachingPlayerView_thenClearsVideoDriverBugPreventer (line 325) | @Test
      method whenSelectingAudioTrack_thenDelegatesToMediaPlayer (line 335) | @Test
      method whenGettingAudioTracks_thenDelegatesToMediaPlayer (line 344) | @Test
      method whenStopping_thenPlayerResourcesAreReleased_andNotListeners (line 352) | @Test
      method whenReleasing_thenPlayerResourcesAreReleased (line 366) | @Test
      method givenAttachedPlayerView_whenStopping_thenPlayerResourcesAreReleased_andNotListeners (line 380) | @Test
      method givenAttachedPlayerView_whenReleasing_thenPlayerResourcesAreReleased (line 396) | @Test
    class GivenPlayerIsAttached (line 413) | public static class GivenPlayerIsAttached extends Base {
      method setUp (line 419) | @Override
      method whenLoadingVideo_thenNotifiesBufferStateListenersThatBufferStarted (line 425) | @Test
      method whenLoadingVideo_thenPreparesVideo (line 432) | @Test
      method whenLoadingVideoWithTimeout_thenNotifiesBufferStateListenersThatBufferStarted (line 439) | @Test
      method whenLoadingVideoWithTimeout_thenPreparesVideo (line 446) | @Test
      method whenLoadingVideoWithTimeout_thenStartsTimeout (line 453) | @Test
      method whenLoadingVideo_thenShowsContainerView (line 460) | @Test
      method whenStartingPlay_thenStartsBeatingHeart (line 467) | @Test
      method whenStartingPlay_thenMediaPlayerStarts (line 474) | @Test
      method whenStartingPlay_thenNotifiesStateListenersThatVideoIsPlaying (line 481) | @Test
      method whenStartingPlayAtVideoPosition_thenStartsBeatingHeart (line 488) | @Test
      method whenStartingPlayAtVideoPosition_thenMediaPlayerStarts (line 497) | @Test
      method whenStartingPlayAtVideoPosition_thenNotifiesStateListenersThatVideoIsPlaying (line 506) | @Test
      method givenPlayerHasPlayedVideo_whenLoadingVideo_thenPlayerIsReleased_andNotListeners (line 515) | @Test
      method givenPlayerHasPlayedVideo_whenLoadingVideoWithTimeout_thenPlayerResourcesAreReleased_andNotListeners (line 528) | @Test
      method givenPlayerHasNotPlayedVideo_whenLoadingVideo_thenPlayerResourcesAreNotReleased (line 541) | @Test
      method givenPlayerHasNotPlayedVideo_whenLoadingVideoWithTimeout_thenPlayerResourcesAreNotReleased (line 553) | @Test
      method givenPositionThatDiffersFromPlayheadPosition_whenStartingPlayAtVideoPosition_thenNotifiesBufferStateListenersThatBufferStarted (line 565) | @Test
      method givenPositionThatDiffersFromPlayheadPosition_whenStartingPlayAtVideoPosition_thenInitialisesPlaybackForSeeking (line 574) | @Test
      method givenPositionThatDiffersFromPlayheadPosition_whenStartingPlayAtVideoPosition_thenSeeksToVideoPosition (line 583) | @Test
      method givenPlayerIsAlreadyPlaying_whenPlaying_thenNotifiesVideoPlaying (line 595) | @Test
      method whenSetRepeating_thenSetRepeating (line 604) | @Test
      method whenSetVolume_thenSetVolumeOnMediaPlayer (line 611) | @Test
      method whenGetVolume_thenReturnMediaPlayerVolume (line 618) | @Test
      method givenPositionThatDiffersFromPlayheadPosition (line 627) | private long givenPositionThatDiffersFromPlayheadPosition() {
      method thenInitialisesPlaybackForSeeking (line 632) | private void thenInitialisesPlaybackForSeeking() {
    class Base (line 641) | public abstract static class Base {
      method onLoadTimeout (line 649) | @Override
      method setUp (line 713) | @Before

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/BuggyVideoDriverPreventerTest.java
  class BuggyVideoDriverPreventerTest (line 16) | public class BuggyVideoDriverPreventerTest {
    method setUp (line 32) | @Before
    method givenVideoDriverIsNotBuggy_whenPreventingVideoDriverBug_thenNothingHappens (line 37) | @Test
    method givenVideoDriverCanBeBuggy_whenPreventingVideoDriverBug_thenABuggyDriverLayoutListenerIsAddedToTheVideoContainer (line 46) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/DelayedActionExecutorTest.java
  class DelayedActionExecutorTest (line 21) | public class DelayedActionExecutorTest {
    method whenActionIsNotPerformedYet_thenMapContainsAction (line 35) | @Test
    method whenPerformingActionAfterDelay_thenRemovesActionFromMap (line 44) | @Test
    method whenPerformingActionAfterDelay_thenPerformsAction (line 53) | @Test
    method givenMultipleQueuedActions_whenClearingActions_thenRemovesAllActions (line 62) | @Test
    method createImmediatelyExecutingHandler (line 74) | private Handler createImmediatelyExecutingHandler() {

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/ErrorFactoryTest.java
  class ErrorFactoryTest (line 20) | @RunWith(Parameterized.class)
    method parameters (line 30) | @Parameterized.Parameters(name = "{0} with detail {1} is mapped from {...
    method mapErrors (line 53) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/ErrorFormatterTest.java
  class ErrorFormatterTest (line 7) | public class ErrorFormatterTest {
    method givenTypeAndExtra_whenFormattingMessage_thenReturnsExpectedMessageFormat (line 12) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/LoadTimeoutTest.java
  class LoadTimeoutTest (line 19) | public class LoadTimeoutTest {
    method setUp (line 32) | @Before
    method whenStartingATimeout_thenAnyPreviouslySetTimeoutRunnableAreRemoved (line 37) | @Test
    method whenStartingATimeout_thenTheTimeoutRunnableIsPosted (line 45) | @Test
    method whenCancelingATimeout_thenTimeoutRunnableIsRemoved (line 53) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/MediaPlayerInformationTest.java
  class MediaPlayerInformationTest (line 17) | public class MediaPlayerInformationTest {
    method setUp (line 27) | @Before
    method givenInternalNuPlayer_whenReadingName_thenReturnsMediaPlayerNuPlayer (line 32) | @Test
    method whenReadingVersion_thenReturnsAndroidBuildVersion (line 41) | @Test
    method whenPlayerType_thenReturnsMediaPlayer (line 49) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/NoPlayerMediaPlayerCreatorTest.java
  class NoPlayerMediaPlayerCreatorTest (line 15) | public class NoPlayerMediaPlayerCreatorTest {
    method setUp (line 29) | @Before
    method whenCreatingMediaPlayer_thenInitialisesPlayer (line 35) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/OnPotentialBuggyDriverLayoutListenerTest.java
  class OnPotentialBuggyDriverLayoutListenerTest (line 16) | public class OnPotentialBuggyDriverLayoutListenerTest {
    method givenStatusIsNotCorrupted_whenALayoutChangeOccurs_thenDoNotForceAlignNativeMediaPlayerStatus (line 29) | @Test
    method givenStatusMightBeNotCorrupted_whenALayoutChangeOccurs_thenForceAlignNativeMediaPlayerStatus (line 38) | @Test
    method onLayoutChange (line 47) | private void onLayoutChange() {

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/PlaybackStateCheckerTest.java
  class PlaybackStateCheckerTest (line 20) | @RunWith(Enclosed.class)
    class CheckingPlaybackState (line 28) | @RunWith(Parameterized.class)
      method parameters (line 38) | @Parameterized.Parameters(name = "MediaPlayer: {0} Playback state: {...
      method CheckingPlaybackState (line 52) | public CheckingPlaybackState(MediaPlayer mediaPlayer,
      method whenCheckingIsInPlaybackState_thenReturnsExpectedState (line 60) | @Test
    class CheckingIsPlaying (line 70) | @RunWith(Parameterized.class)
      method parameters (line 85) | @Parameterized.Parameters(name = "MediaPlayer: {0} mediaPlayer.isPla...
      method CheckingIsPlaying (line 108) | public CheckingIsPlaying(MediaPlayer mediaPlayer,
      method whenCheckingIsPlaying_thenReturnsExpectedState (line 120) | @Test

FILE: core/src/test/java/com/novoda/noplayer/internal/mediaplayer/PlayerCheckerTest.java
  class PlayerCheckerTest (line 14) | public class PlayerCheckerTest {
    method setUp (line 25) | @Before
    method givenTheUserIsOnLollipopWhenTheAwesomePlayerPersistPropertyIsPresentThenAwesomePlayerIsDetected (line 30) | @Test
    method givenTheUserIsOnLollipopWhenTheAwesomePlayerMediaPropertyIsPresentThenAwesomePlayerIsDetected (line 39) | @Test
    method givenTheUserIsOnLollipopWhenNoAwesomePlayerMediaPropertyIsPresentThenNuPlayerIsDetected (line 48) | @Test
    method givenTheUserIsOnKitkatWhenTheNuPlayerPropertyIsPresentThenNuPlayerIsDetected (line 57) | @Test
    method givenTheUserIsOnKitkatWhenNoNuPlayerMediaPropertyIsPresentThenAwesomePlayerIsDetected (line 66) | @Test
    method givenTheUserIsNotAbleToReadSystemPropertiesWhenFetchingThePlayerTypeThenUnknownPlayerIsDetected (line 75) | @Test
    method givenTheUserIsOnOSVersion (line 84) | private void givenTheUserIsOnOSVersion(int deviceOSVersion) {
    method whenPropertyisPresent (line 88) | private void whenPropertyisPresent(String property) throws SystemPrope...
    method whenNoPlayerPropertiesArePresent (line 92) | private void whenNoPlayerPropertiesArePresent() {
    method thenPlayerTypeIs (line 96) | private void thenPlayerTypeIs(AndroidMediaPlayerType playerType) {

FILE: core/src/test/java/com/novoda/noplayer/model/AudioTracksTest.java
  class AudioTracksTest (line 12) | public class AudioTracksTest {
    method givenAudioTracks_whenGettingTrack_thenReturnsTrack (line 19) | @Test
    method givenAudioTracks_whenGettingSize_thenReturnsSize (line 28) | @Test

FILE: core/src/test/java/com/novoda/noplayer/model/EitherTest.java
  class EitherTest (line 10) | @RunWith(MockitoJUnitRunner.class)
    method givenEitherContainsLeft_whenApplyingConsumers_thenRunsLeftConsumerWithCorrectValue (line 18) | @Test
    method givenEitherContainsRight_whenApplyingConsumers_thenRunsRightConsumerWithCorrectValue (line 28) | @Test

FILE: core/src/test/java/com/novoda/noplayer/model/PlayerAudioTrackFixture.java
  class PlayerAudioTrackFixture (line 5) | public class PlayerAudioTrackFixture {
    method aPlayerAudioTrack (line 16) | public static PlayerAudioTrackFixture aPlayerAudioTrack() {
    method withGroupIndex (line 20) | public PlayerAudioTrackFixture withGroupIndex(int groupIndex) {
    method withFormatIndex (line 25) | public PlayerAudioTrackFixture withFormatIndex(int formatIndex) {
    method withTrackId (line 30) | public PlayerAudioTrackFixture withTrackId(String trackId) {
    method withLanguage (line 35) | public PlayerAudioTrackFixture withLanguage(String language) {
    method withMimeType (line 40) | public PlayerAudioTrackFixture withMimeType(String mimeType) {
    method withNumberOfChannels (line 45) | public PlayerAudioTrackFixture withNumberOfChannels(int numberOfChanne...
    method withFrequency (line 50) | public PlayerAudioTrackFixture withFrequency(int frequency) {
    method withAudioTrackType (line 55) | public PlayerAudioTrackFixture withAudioTrackType(AudioTrackType audio...
    method build (line 60) | public PlayerAudioTrack build() {

FILE: core/src/test/java/com/novoda/noplayer/model/PlayerVideoTrackFixture.java
  class PlayerVideoTrackFixture (line 5) | public class PlayerVideoTrackFixture {
    method aPlayerVideoTrack (line 16) | public static PlayerVideoTrackFixture aPlayerVideoTrack() {
    method PlayerVideoTrackFixture (line 20) | private PlayerVideoTrackFixture() {
    method withGroupIndex (line 24) | public PlayerVideoTrackFixture withGroupIndex(int groupIndex) {
    method withFormatIndex (line 29) | public PlayerVideoTrackFixture withFormatIndex(int formatIndex) {
    method withId (line 34) | public PlayerVideoTrackFixture withId(String id) {
    method withContentType (line 39) | public PlayerVideoTrackFixture withContentType(ContentType contentType) {
    method withWidth (line 44) | public PlayerVideoTrackFixture withWidth(int width) {
    method withHeight (line 49) | public PlayerVideoTrackFixture withHeight(int height) {
    method withFps (line 54) | public PlayerVideoTrackFixture withFps(int fps) {
    method withBitrate (line 59) | public PlayerVideoTrackFixture withBitrate(int bitrate) {
    method build (line 64) | public PlayerVideoTrack build() {

FILE: core/src/test/java/utils/ExceptionMatcher.java
  class ExceptionMatcher (line 6) | public class ExceptionMatcher extends BaseMatcher<Exception> {
    method matches (line 11) | public static ExceptionMatcher matches(String message, Class<? extends...
    method ExceptionMatcher (line 15) | private ExceptionMatcher(String expectedMessage, Class<? extends Excep...
    method matches (line 20) | @Override
    method describeTo (line 26) | @Override

FILE: demo/src/main/java/com/novoda/demo/AndroidControllerView.java
  class AndroidControllerView (line 14) | public class AndroidControllerView extends LinearLayout implements Contr...
    method AndroidControllerView (line 22) | public AndroidControllerView(@NonNull Context context, @Nullable Attri...
    method AndroidControllerView (line 26) | public AndroidControllerView(Context context, @Nullable AttributeSet a...
    method setOrientation (line 31) | @Override
    method onFinishInflate (line 36) | @Override
    method setPaused (line 48) | @Override
    method setPlaying (line 53) | @Override
    method updateContentProgress (line 58) | @Override
    method updateBufferProgress (line 63) | @Override
    method updateElapsedTime (line 68) | @Override
    method updateTimeRemaining (line 73) | @Override
    method setTogglePlayPauseAction (line 78) | @Override
    method setSeekAction (line 88) | @Override
    method setVolumeOn (line 110) | @Override
    method setVolumeOff (line 115) | @Override
    method setToggleVolumeOnOffAction (line 120) | @Override

FILE: demo/src/main/java/com/novoda/demo/ControllerView.java
  type ControllerView (line 3) | interface ControllerView {
    method setPaused (line 5) | void setPaused();
    method setPlaying (line 7) | void setPlaying();
    method updateContentProgress (line 9) | void updateContentProgress(int progress);
    method updateBufferProgress (line 11) | void updateBufferProgress(int buffer);
    method updateElapsedTime (line 13) | void updateElapsedTime(String elapsedTime);
    method updateTimeRemaining (line 15) | void updateTimeRemaining(String timeRemaining);
    method setTogglePlayPauseAction (line 17) | void setTogglePlayPauseAction(TogglePlayPauseAction togglePlayPauseAct...
    method setSeekAction (line 19) | void setSeekAction(SeekAction seekAction);
    method setVolumeOn (line 21) | void setVolumeOn();
    method setVolumeOff (line 23) | void setVolumeOff();
    method setToggleVolumeOnOffAction (line 25) | void setToggleVolumeOnOffAction(ToggleVolumeOnOffAction toggleVolumeOn...
    type TogglePlayPauseAction (line 27) | interface TogglePlayPauseAction {
      method perform (line 29) | void perform();
    type SeekAction (line 32) | interface SeekAction {
      method perform (line 34) | void perform(int progress, int max);
    type ToggleVolumeOnOffAction (line 37) | interface ToggleVolumeOnOffAction {
      method perform (line 39) | void perform();

FILE: demo/src/main/java/com/novoda/demo/DataPostingModularDrm.java
  class DataPostingModularDrm (line 6) | class DataPostingModularDrm implements StreamingModularDrm {
    method DataPostingModularDrm (line 10) | DataPostingModularDrm(String url) {
    method executeKeyRequest (line 14) | @Override

FILE: demo/src/main/java/com/novoda/demo/DemoPresenter.java
  class DemoPresenter (line 11) | class DemoPresenter {
    method DemoPresenter (line 18) | DemoPresenter(ControllerView controllerView, NoPlayer noPlayer, Listen...
    method startPresenting (line 25) | void startPresenting(Uri uri, Options options) {
    method onPrepared (line 39) | @Override
    method onVideoPlaying (line 46) | @Override
    method onVideoPaused (line 51) | @Override
    method onVideoStopped (line 56) | @Override
    method onBeat (line 63) | @Override
    method updateProgress (line 74) | private void updateProgress(long positionInMillis, long durationInMill...
    method updateTiming (line 82) | private void updateTiming(long positionInMillis, long durationInMillis) {
    method perform (line 90) | @Override
    method perform (line 101) | @Override
    method perform (line 109) | @Override
    method stopPresenting (line 122) | void stopPresenting() {

FILE: demo/src/main/java/com/novoda/demo/DialogCreator.java
  class DialogCreator (line 18) | class DialogCreator {
    method DialogCreator (line 27) | DialogCreator(Context context, NoPlayer noPlayer) {
    method showVideoSelectionDialog (line 32) | void showVideoSelectionDialog() {
    method mapVideoTrackToLabel (line 54) | private List<String> mapVideoTrackToLabel(List<PlayerVideoTrack> video...
    method showAudioSelectionDialog (line 64) | void showAudioSelectionDialog() {
    method mapAudioTrackToLabel (line 85) | private List<String> mapAudioTrackToLabel(AudioTracks audioTracks) {
    method showSubtitleSelectionDialog (line 100) | void showSubtitleSelectionDialog() {
    method mapSubtitleTrackToLabel (line 121) | private List<String> mapSubtitleTrackToLabel(List<PlayerSubtitleTrack>...

FILE: demo/src/main/java/com/novoda/demo/HttpClient.java
  class HttpClient (line 11) | final class HttpClient {
    method HttpClient (line 16) | private HttpClient() {
    method post (line 20) | static byte[] post(String url, byte[] data) {
    method release (line 41) | private static void release(HttpURLConnection connection) {
    method readResponseFrom (line 47) | private static byte[] readResponseFrom(URLConnection connection) throw...
    method release (line 70) | private static void release(InputStream inputStream, ByteArrayOutputSt...
    class HttpClientException (line 80) | private static class HttpClientException extends RuntimeException {
      method HttpClientException (line 82) | HttpClientException(Throwable cause) {

FILE: demo/src/main/java/com/novoda/demo/MainActivity.java
  class MainActivity (line 20) | public class MainActivity extends Activity {
    method onCreate (line 33) | @Override
    method onStart (line 71) | @Override
    method getMaxVideoBitrate (line 84) | private int getMaxVideoBitrate() {
    method onClick (line 92) | @Override
    method onClick (line 104) | @Override
    method onClick (line 111) | @Override
    method onCheckedChanged (line 122) | @Override
    method onStop (line 132) | @Override

FILE: demo/src/main/java/com/novoda/demo/ProgressCalculator.java
  class ProgressCalculator (line 3) | final class ProgressCalculator {
    method ProgressCalculator (line 8) | private ProgressCalculator() {
    method progressAsIncrements (line 12) | static int progressAsIncrements(long positionInMillis, long durationIn...
    method bufferAsIncrements (line 17) | static int bufferAsIncrements(int bufferPercentage) {
    method seekToPosition (line 21) | static long seekToPosition(long durationInMillis, int progress, int ma...

FILE: demo/src/main/java/com/novoda/demo/TimeFormatter.java
  class TimeFormatter (line 6) | final class TimeFormatter {
    method TimeFormatter (line 8) | private TimeFormatter() {
    method asHoursMinutesSeconds (line 12) | static String asHoursMinutesSeconds(long timeInMillis) {

FILE: demo/src/test/java/com/novoda/demo/TimeFormatterTest.java
  class TimeFormatterTest (line 7) | public class TimeFormatterTest {
    method givenOverOneHourInMillis_whenFormatting_thenReturnsAsHoursMinutesSeconds (line 9) | @Test
    method givenLessThanOneHourInMillis_whenFormatting_thenReturnsAsMinutesAndSeconds (line 18) | @Test
Condensed preview — 266 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (807K chars).
[
  {
    "path": ".github/contributing.md",
    "chars": 2051,
    "preview": "# Contributing to Novoda's Open Source projects\n\nWe encourage everyone inside and outside Novoda to contribute to the pr"
  },
  {
    "path": ".github/issue_template.md",
    "chars": 371,
    "preview": "#### Problem\n\n_Explain the problem that requires addressing_\n\n#### Potential Solution\n\n_Explain high level any potential"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 457,
    "preview": "## Problem\n\n_Explain what is requested to do, or the problem you had to fix_\n\n## Solution\n\n_Explain high level how have "
  },
  {
    "path": ".github/workflows/pull-request-builder.yml",
    "chars": 1245,
    "preview": "name: Pull Request Builder\n\non:\n  pull_request:\n\nenv:\n  GRADLE_USER_HOME: .gradle\n\njobs:\n\n  build:\n    runs-on: ubuntu-l"
  },
  {
    "path": ".gitignore",
    "chars": 615,
    "preview": "# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbi"
  },
  {
    "path": ".idea/codeStyleSettings.xml",
    "chars": 10610,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectCodeStyleSettingsManager\">\n    <o"
  },
  {
    "path": ".idea/compiler.xml",
    "chars": 423,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <resourceExt"
  },
  {
    "path": ".idea/encodings.xml",
    "chars": 159,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\">\n    <file url=\"PROJECT\" chars"
  },
  {
    "path": ".idea/vcs.xml",
    "chars": 180,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping dire"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "NOTICE",
    "chars": 577,
    "preview": "   Copyright 2017 Novoda\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this fil"
  },
  {
    "path": "README.md",
    "chars": 4135,
    "preview": "# This project is no longer under maintenance - 13/7/2020\nUpgrades to latests version of `ExoPlayer` requires a signific"
  },
  {
    "path": "build.gradle",
    "chars": 1254,
    "preview": "allprojects {\n    version = '4.5.4'\n}\n\ndef teamPropsFile(propsFile) {\n    def teamPropsDir = rootProject.file('team-prop"
  },
  {
    "path": "core/build.gradle",
    "chars": 2763,
    "preview": "apply plugin: 'com.android.library'\napply plugin: 'bintray-release'\napply plugin: 'jacoco'\napply plugin: 'com.novoda.bui"
  },
  {
    "path": "core/src/main/AndroidManifest.xml",
    "chars": 430,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"com.novoda.noplayer\">\n\n  <uses-permissio"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/AndroidMediaPlayerCapabilities.java",
    "chars": 431,
    "preview": "package com.novoda.noplayer;\n\nimport com.novoda.noplayer.drm.DrmType;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/AspectRatioChangeCalculator.java",
    "chars": 749,
    "preview": "package com.novoda.noplayer;\n\nclass AspectRatioChangeCalculator {\n\n    private final Listener listener;\n\n    AspectRatio"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/ContentType.java",
    "chars": 86,
    "preview": "package com.novoda.noplayer;\n\npublic enum ContentType {\n    H264,\n    DASH,\n    HLS\n}\n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/DetailErrorType.java",
    "chars": 2960,
    "preview": "package com.novoda.noplayer;\n\n/**\n * Assume all errors are thrown by Exo Player, MEDIA_PLAYER prefix will\n * indicate th"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/ExoPlayerCapabilities.java",
    "chars": 505,
    "preview": "package com.novoda.noplayer;\n\nimport com.novoda.noplayer.drm.DrmType;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/Listeners.java",
    "chars": 4767,
    "preview": "package com.novoda.noplayer;\n\npublic interface Listeners {\n\n    /**\n     * Add an {@link NoPlayer.ErrorListener} to be n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/NoPlayer.java",
    "chars": 11367,
    "preview": "package com.novoda.noplayer;\n\nimport android.net.Uri;\nimport android.support.annotation.FloatRange;\n\nimport com.novoda.n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/NoPlayerCreator.java",
    "chars": 3162,
    "preview": "package com.novoda.noplayer;\n\nimport android.content.Context;\n\nimport com.novoda.noplayer.drm.DrmHandler;\nimport com.nov"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/NoPlayerError.java",
    "chars": 733,
    "preview": "package com.novoda.noplayer;\n\npublic class NoPlayerError implements NoPlayer.PlayerError {\n\n    private final PlayerErro"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/NoPlayerView.java",
    "chars": 3176,
    "preview": "package com.novoda.noplayer;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.Surf"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/Options.java",
    "chars": 3815,
    "preview": "package com.novoda.noplayer;\n\nimport com.novoda.noplayer.internal.utils.Optional;\n\n/**\n * Options to customise the under"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/OptionsBuilder.java",
    "chars": 3910,
    "preview": "package com.novoda.noplayer;\n\nimport android.net.Uri;\n\nimport com.novoda.noplayer.internal.utils.Optional;\n\n/**\n * Build"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerBuilder.java",
    "chars": 5849,
    "preview": "package com.novoda.noplayer;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Looper;\n\nimpo"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerCapabilities.java",
    "chars": 145,
    "preview": "package com.novoda.noplayer;\n\nimport com.novoda.noplayer.drm.DrmType;\n\ninterface PlayerCapabilities {\n\n    boolean suppo"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerErrorType.java",
    "chars": 207,
    "preview": "package com.novoda.noplayer;\n\npublic enum PlayerErrorType {\n\n    SOURCE,\n    CONNECTIVITY,\n    DRM,\n    CONTENT_DECRYPTI"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerInformation.java",
    "chars": 151,
    "preview": "package com.novoda.noplayer;\n\npublic interface PlayerInformation {\n\n    PlayerType getPlayerType();\n\n    String getVersi"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerState.java",
    "chars": 238,
    "preview": "package com.novoda.noplayer;\n\npublic interface PlayerState {\n\n    boolean isPlaying();\n\n    int videoWidth();\n\n    int v"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerSurfaceHolder.java",
    "chars": 1913,
    "preview": "package com.novoda.noplayer;\n\nimport android.support.annotation.Nullable;\nimport android.view.SurfaceView;\nimport androi"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerType.java",
    "chars": 996,
    "preview": "package com.novoda.noplayer;\n\nimport com.novoda.noplayer.drm.DrmType;\n\npublic enum PlayerType {\n    MEDIA_PLAYER(new And"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerView.java",
    "chars": 445,
    "preview": "package com.novoda.noplayer;\n\nimport android.view.View;\nimport com.novoda.noplayer.model.TextCues;\n\npublic interface Pla"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/PlayerViewSurfaceHolder.java",
    "chars": 2412,
    "preview": "package com.novoda.noplayer;\n\nimport android.graphics.SurfaceTexture;\nimport android.support.annotation.Nullable;\nimport"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/SubtitlePainter.java",
    "chars": 18682,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/SubtitleView.java",
    "chars": 2336,
    "preview": "package com.novoda.noplayer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.Attrib"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/SurfaceRequester.java",
    "chars": 367,
    "preview": "package com.novoda.noplayer;\n\nimport android.view.Surface;\nimport android.view.SurfaceHolder;\nimport com.novoda.noplayer"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/UnableToCreatePlayerException.java",
    "chars": 1388,
    "preview": "package com.novoda.noplayer;\n\nimport com.novoda.noplayer.drm.DrmType;\nimport com.novoda.noplayer.internal.utils.AndroidD"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/drm/DownloadedModularDrm.java",
    "chars": 168,
    "preview": "package com.novoda.noplayer.drm;\n\nimport com.novoda.noplayer.model.KeySetId;\n\npublic interface DownloadedModularDrm exte"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/drm/DrmHandler.java",
    "chars": 308,
    "preview": "package com.novoda.noplayer.drm;\n\n@SuppressWarnings({\n        \"checkstyle:interfaceistype\",\n        \"PMD.AvoidConstantsI"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/drm/DrmType.java",
    "chars": 149,
    "preview": "package com.novoda.noplayer.drm;\n\npublic enum DrmType {\n    NONE,\n    WIDEVINE_CLASSIC,\n    WIDEVINE_MODULAR_STREAM,\n   "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/drm/ModularDrmKeyRequest.java",
    "chars": 1253,
    "preview": "package com.novoda.noplayer.drm;\n\nimport java.util.Arrays;\n\npublic class ModularDrmKeyRequest {\n\n    private final Strin"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/drm/ModularDrmProvisionRequest.java",
    "chars": 1283,
    "preview": "package com.novoda.noplayer.drm;\n\nimport java.util.Arrays;\n\npublic class ModularDrmProvisionRequest {\n\n    private final"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/drm/StreamingModularDrm.java",
    "chars": 829,
    "preview": "package com.novoda.noplayer.drm;\n\npublic interface StreamingModularDrm extends DrmHandler {\n\n    byte[] executeKeyReques"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/CssParser.java",
    "chars": 11851,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttCueParser.java",
    "chars": 22983,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttDecoder.java",
    "chars": 4802,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/external/exoplayer/text/webvtt/WebvttSubtitle.java",
    "chars": 4040,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/external/exoplayer/util/ColorParser.java",
    "chars": 11121,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/Clock.java",
    "chars": 145,
    "preview": "package com.novoda.noplayer.internal;\n\nimport java.io.Serializable;\n\npublic interface Clock extends Serializable {\n\n    "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/Heart.java",
    "chars": 2354,
    "preview": "package com.novoda.noplayer.internal;\n\nimport android.os.Handler;\n\nimport com.novoda.noplayer.NoPlayer;\n\n@SuppressWarnin"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/SystemClock.java",
    "chars": 185,
    "preview": "package com.novoda.noplayer.internal;\n\npublic class SystemClock implements Clock {\n\n    @Override\n    public long getCur"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/drm/provision/HttpPostingProvisionExecutor.java",
    "chars": 1280,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\nimport com.novoda.noplayer.drm.ModularDrmProvisionRequest;\n\nimport "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/drm/provision/HttpUrlConnectionPoster.java",
    "chars": 1622,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\ni"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisionExecutor.java",
    "chars": 285,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\nimport com.novoda.noplayer.drm.ModularDrmProvisionRequest;\n\nimport "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisionExecutorCreator.java",
    "chars": 381,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\npublic class ProvisionExecutorCreator {\n\n    public ProvisionExecut"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/drm/provision/ProvisioningCapabilities.java",
    "chars": 570,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\nimport android.os.Build;\nimport android.support.annotation.VisibleF"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/drm/provision/UnableToProvisionException.java",
    "chars": 398,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\nimport android.os.Build;\n\npublic class UnableToProvisionException e"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/BandwidthMeterCreator.java",
    "chars": 515,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.content.Context;\n\nimport com.google.android.exoplayer2.u"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/CompositeTrackSelector.java",
    "chars": 3955,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.SimpleExoPlayer;\nimport com.google"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/CompositeTrackSelectorCreator.java",
    "chars": 2511,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.trackselection.AdaptiveTrackSelect"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerCreator.java",
    "chars": 2210,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.content.Context;\nimport android.support.annotation.NonNu"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerCueMapper.java",
    "chars": 1215,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.text.Cue;\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerFacade.java",
    "chars": 9275,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.net.Uri;\nimport android.support.annotation.Nullable;\n\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerInformation.java",
    "chars": 530,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.ExoPlayerLibraryInfo;\nimport com.n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerTwoImpl.java",
    "chars": 10356,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.net.Uri;\nimport android.support.annotation.Nullable;\nimp"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreator.java",
    "chars": 4699,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.content.Context;\nimport android.os.Handler;\n\nimport com."
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/RendererTypeRequester.java",
    "chars": 132,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\npublic interface RendererTypeRequester {\n\n    int getRendererTypeFor(in"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/RendererTypeRequesterCreator.java",
    "chars": 426,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.SimpleExoPlayer;\n\nclass RendererTy"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/SecurityDowngradingCodecSelector.java",
    "chars": 1757,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.mediacodec.MediaCodecInfo;\nimport "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/SimpleRenderersFactory.java",
    "chars": 15960,
    "preview": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/TextRendererOutput.java",
    "chars": 1283,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.text.Cue;\nimport com.google.androi"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DownloadDrmSessionCreator.java",
    "chars": 1222,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.os.Handler;\n\nimport com.google.android.exoplayer2.dr"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreator.java",
    "chars": 557,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.support.annotation.Nullable;\n\nimport com.google.andr"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreatorException.java",
    "chars": 418,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport com.novoda.noplayer.drm.DrmType;\n\npublic final class DrmSess"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreatorFactory.java",
    "chars": 3036,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.os.Build;\nimport android.os.Handler;\n\nimport com.nov"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/FrameworkDrmSession.java",
    "chars": 276,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport com.google.android.exoplayer2.drm.DrmSession;\nimport com.goo"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/FrameworkMediaDrmCreator.java",
    "chars": 840,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport com.google.android.exoplayer2.drm.FrameworkMediaDrm;\nimport "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/InvalidDrmSession.java",
    "chars": 1828,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.support.annotation.Nullable;\n\nimport com.google.andr"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/LocalDrmSession.java",
    "chars": 2401,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.support.annotation.Nullable;\n\nimport com.google.andr"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/LocalDrmSessionManager.java",
    "chars": 3196,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimpor"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/NoDrmSessionCreator.java",
    "chars": 615,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.support.annotation.Nullable;\n\nimport com.google.andr"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/ProvisioningModularDrmCallback.java",
    "chars": 1336,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport com.google.android.exoplayer2.drm.ExoMediaDrm;\nimport com.go"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/SessionId.java",
    "chars": 1272,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport java.util.Arrays;\n\nfinal class SessionId {\n\n    private fina"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/drm/StreamingDrmSessionCreator.java",
    "chars": 1882,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.os.Handler;\n\nimport com.google.android.exoplayer2.dr"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/ErrorFormatter.java",
    "chars": 854,
    "preview": "package com.novoda.noplayer.internal.exoplayer.error;\n\nimport android.media.MediaCodec;\nimport android.os.Build;\nimport "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/ExoPlayerErrorMapper.java",
    "chars": 1160,
    "preview": "package com.novoda.noplayer.internal.exoplayer.error;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nimport"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/RendererErrorMapper.java",
    "chars": 7478,
    "preview": "package com.novoda.noplayer.internal.exoplayer.error;\n\nimport android.media.MediaCodec;\n\nimport com.google.android.exopl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/SourceErrorMapper.java",
    "chars": 9376,
    "preview": "package com.novoda.noplayer.internal.exoplayer.error;\n\nimport com.google.android.exoplayer2.ParserException;\nimport com."
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/error/UnexpectedErrorMapper.java",
    "chars": 1800,
    "preview": "package com.novoda.noplayer.internal.exoplayer.error;\n\nimport android.media.MediaCodec;\nimport android.os.Build;\n\nimport"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/AnalyticsListenerForwarder.java",
    "chars": 19461,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport android.view.Surface;\n\nimport com.google.android.exopl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/BitrateForwarder.java",
    "chars": 3611,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport android.support.annotation.Nullable;\n\nimport com.googl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/BufferStateForwarder.java",
    "chars": 2177,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/DrmSessionInfoForwarder.java",
    "chars": 1472,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.drm.DefaultDrmSessionEve"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/EventInfoForwarder.java",
    "chars": 4629,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/EventListener.java",
    "chars": 2923,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ExoPlayerDrmSessionEventListener.java",
    "chars": 1222,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.drm.DefaultDrmSessionEve"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ExoPlayerForwarder.java",
    "chars": 3322,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.analytics.AnalyticsListe"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ExoPlayerVideoListener.java",
    "chars": 891,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.video.VideoListener;\n\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/ForwarderInformation.java",
    "chars": 5356,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nclass ForwarderInformation {\n\n    static final class Paramete"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/MediaSourceEventForwarder.java",
    "chars": 7221,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport android.support.annotation.Nullable;\n\nimport com.googl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/NoPlayerAnalyticsListener.java",
    "chars": 11369,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport android.view.Surface;\n\nimport com.google.android.exopl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/NoPlayerMediaSourceEventListener.java",
    "chars": 4136,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport android.support.annotation.Nullable;\n\nimport com.googl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/OnCompletionForwarder.java",
    "chars": 2054,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/OnCompletionStateChangedForwarder.java",
    "chars": 2066,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/OnPrepareForwarder.java",
    "chars": 2286,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/PlayerOnErrorForwarder.java",
    "chars": 2144,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.ExoPlaybackException;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/forwarder/VideoSizeChangedForwarder.java",
    "chars": 812,
    "preview": "package com.novoda.noplayer.internal.exoplayer.forwarder;\n\nimport com.google.android.exoplayer2.video.VideoListener;\nimp"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/AudioTrackType.java",
    "chars": 555,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\npublic enum AudioTrackType {\n\n    MAIN(1),\n    ALTERNATIVE("
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerAudioTrackSelector.java",
    "chars": 2843,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nimport com.google.android.exoplayer2.Format;\nimport com.goo"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerMappedTrackInfo.java",
    "chars": 781,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nimport com.google.android.exoplayer2.source.TrackGroupArray"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerSubtitleTrackSelector.java",
    "chars": 2584,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nimport com.google.android.exoplayer2.Format;\nimport com.goo"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerTrackSelector.java",
    "chars": 4560,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nimport com.google.android.exoplayer2.RendererCapabilities;\n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/ExoPlayerVideoTrackSelector.java",
    "chars": 3935,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nimport com.google.android.exoplayer2.Format;\nimport com.goo"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/MediaSourceFactory.java",
    "chars": 5335,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nimport android.content.Context;\nimport android.net.Uri;\nimp"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/RendererTrackIndexExtractor.java",
    "chars": 866,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nimport com.google.android.exoplayer2.C;\nimport com.novoda.n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/TrackType.java",
    "chars": 111,
    "preview": "package com.novoda.noplayer.internal.exoplayer.mediasource;\n\nenum TrackType {\n    AUDIO,\n    VIDEO,\n    TEXT\n}\n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/BitrateChangedListeners.java",
    "chars": 874,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\nimport com.novoda.noplayer.model.B"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/BufferStateListeners.java",
    "chars": 921,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport java.util.Set;\nimport java"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/CompletionListeners.java",
    "chars": 907,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport java.util.Set;\nimport java"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/DroppedFramesListeners.java",
    "chars": 899,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport java.util.Set;\nimport java"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/ErrorListeners.java",
    "chars": 723,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport java.util.Set;\nimport java"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/HeartbeatCallbacks.java",
    "chars": 796,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport java.util.Set;\nimport java"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/InfoListeners.java",
    "chars": 807,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport java.util.Map;\nimport java"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/PlayerListenersHolder.java",
    "chars": 6458,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.Listeners;\nimport com.novoda.noplayer.NoPlay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/PreparedListeners.java",
    "chars": 974,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\nimport com.novoda.noplayer.PlayerS"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/StateChangedListeners.java",
    "chars": 1874,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\nimport com.novoda.noplayer.interna"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/listeners/VideoSizeChangedListeners.java",
    "chars": 922,
    "preview": "package com.novoda.noplayer.internal.listeners;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport java.util.Set;\nimport java"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerAudioTrackSelector.java",
    "chars": 2231,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplayer.interna"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerFacade.java",
    "chars": 13516,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.content.Context;\nimport android.media.AudioManager;\nim"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerImpl.java",
    "chars": 14305,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.media.MediaPlayer;\nimport android.net.Uri;\nimport andr"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/AndroidMediaPlayerType.java",
    "chars": 316,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nenum AndroidMediaPlayerType {\n\n    AWESOME(\"AwesomePlayer\"),\n    NU(\""
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/BuggyVideoDriverPreventer.java",
    "chars": 1621,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.view.View;\n\nimport com.novoda.noplayer.NoPlayer;\n\n/**\n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/CheckBufferHeartbeatCallback.java",
    "chars": 2087,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport com.novoda.noplayer.NoPlayer;\n\npublic class CheckBufferHeartbe"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/DelayedActionExecutor.java",
    "chars": 1146,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.os.Handler;\n\nimport java.util.Iterator;\nimport java.ut"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/ErrorFactory.java",
    "chars": 3603,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplayer.DetailE"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/ErrorFormatter.java",
    "chars": 242,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nfinal class ErrorFormatter {\n\n    private ErrorFormatter() {\n    }\n\n "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/MediaPlayerCreator.java",
    "chars": 194,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.media.MediaPlayer;\n\nclass MediaPlayerCreator {\n\n    Me"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/MediaPlayerInformation.java",
    "chars": 756,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.os.Build;\n\nimport com.novoda.noplayer.PlayerInformatio"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/MediaPlayerTypeReader.java",
    "chars": 2432,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.os.Build;\n\nclass MediaPlayerTypeReader {\n\n    private "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/NoPlayerMediaPlayerCreator.java",
    "chars": 2734,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.content.Context;\nimport android.os.Build;\nimport andro"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/NoPlayerTrackInfo.java",
    "chars": 402,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.media.MediaPlayer;\n\nclass NoPlayerTrackInfo {\n\n    pri"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/NoPlayerTrackInfos.java",
    "chars": 887,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport java.util.List;\n\nclass NoPlayerTrackInfos {\n\n    private final"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/OnPotentialBuggyDriverLayoutListener.java",
    "chars": 905,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.view.View;\n\nimport com.novoda.noplayer.NoPlayer;\n\nclas"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/PlaybackStateChecker.java",
    "chars": 995,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.media.MediaPlayer;\n\nimport static com.novoda.noplayer."
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/SystemProperties.java",
    "chars": 1505,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.annotation.SuppressLint;\n\nimport java.lang.reflect.Inv"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/TrackInfosFactory.java",
    "chars": 629,
    "preview": "package com.novoda.noplayer.internal.mediaplayer;\n\nimport android.media.MediaPlayer;\n\nimport java.util.ArrayList;\nimport"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/BufferHeartbeatListener.java",
    "chars": 687,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport com.novoda.noplayer.NoPlayer;\nimport com.novoda.nopl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/BufferInfoForwarder.java",
    "chars": 868,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport com.novoda.noplayer.NoPlayer;\nimport com.novoda.nopl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/BufferOnPreparedListener.java",
    "chars": 539,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/CompletionForwarder.java",
    "chars": 521,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/CompletionInfoForwarder.java",
    "chars": 690,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/CompletionStateChangedForwarder.java",
    "chars": 561,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/ErrorForwarder.java",
    "chars": 876,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/ErrorInfoForwarder.java",
    "chars": 847,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/HeartBeatListener.java",
    "chars": 891,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport com.novoda.noplayer.internal.mediaplayer.CheckBuffer"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerCompletionListener.java",
    "chars": 639,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport java.util.List;\ni"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerErrorListener.java",
    "chars": 719,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport java.util.List;\ni"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerForwarder.java",
    "chars": 3000,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/MediaPlayerPreparedListener.java",
    "chars": 625,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport java.util.List;\ni"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/OnPreparedForwarder.java",
    "chars": 660,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/OnPreparedInfoForwarder.java",
    "chars": 684,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/VideoSizeChangedForwarder.java",
    "chars": 1268,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/VideoSizeChangedInfoForwarder.java",
    "chars": 883,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport com.novoda.noplay"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/mediaplayer/forwarder/VideoSizeChangedListener.java",
    "chars": 722,
    "preview": "package com.novoda.noplayer.internal.mediaplayer.forwarder;\n\nimport android.media.MediaPlayer;\n\nimport java.util.List;\ni"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/utils/AndroidDeviceVersion.java",
    "chars": 650,
    "preview": "package com.novoda.noplayer.internal.utils;\n\nimport android.os.Build;\n\npublic class AndroidDeviceVersion {\n\n    private "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/utils/NoPlayerLog.java",
    "chars": 4108,
    "preview": "package com.novoda.noplayer.internal.utils;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.L"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/internal/utils/Optional.java",
    "chars": 2163,
    "preview": "package com.novoda.noplayer.internal.utils;\n\npublic final class Optional<T> {\n\n    @SuppressWarnings(\"unchecked\")  // Ty"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/AudioTracks.java",
    "chars": 1454,
    "preview": "package com.novoda.noplayer.model;\n\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\n\npub"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/Bitrate.java",
    "chars": 857,
    "preview": "package com.novoda.noplayer.model;\n\npublic final class Bitrate {\n\n    private static final int KILOBIT = 1000;\n\n    priv"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/Either.java",
    "chars": 1153,
    "preview": "package com.novoda.noplayer.model;\n\npublic abstract class Either<L, R> {\n\n    public static <L, R> Either<L, R> left(L l"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/KeySetId.java",
    "chars": 1151,
    "preview": "package com.novoda.noplayer.model;\n\nimport java.util.Arrays;\n\npublic final class KeySetId {\n\n    private final byte[] ke"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/LoadTimeout.java",
    "chars": 1367,
    "preview": "package com.novoda.noplayer.model;\n\nimport android.os.Handler;\n\nimport com.novoda.noplayer.internal.Clock;\nimport com.no"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/NoPlayerCue.java",
    "chars": 5228,
    "preview": "package com.novoda.noplayer.model;\n\nimport android.graphics.Bitmap;\nimport android.text.Layout.Alignment;\n\npublic class "
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/PlayerAudioTrack.java",
    "chars": 3865,
    "preview": "package com.novoda.noplayer.model;\n\nimport com.novoda.noplayer.internal.exoplayer.mediasource.AudioTrackType;\n\npublic cl"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/PlayerSubtitleTrack.java",
    "chars": 3258,
    "preview": "package com.novoda.noplayer.model;\n\npublic class PlayerSubtitleTrack {\n\n    private final int groupIndex;\n    private fi"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/PlayerVideoTrack.java",
    "chars": 3096,
    "preview": "package com.novoda.noplayer.model;\n\nimport com.novoda.noplayer.ContentType;\n\npublic class PlayerVideoTrack {\n\n    privat"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/TextCues.java",
    "chars": 1125,
    "preview": "package com.novoda.noplayer.model;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic final class TextCues {\n"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/model/Timeout.java",
    "chars": 537,
    "preview": "package com.novoda.noplayer.model;\n\nimport java.util.concurrent.TimeUnit;\n\npublic final class Timeout {\n\n    private sta"
  },
  {
    "path": "core/src/main/java/com/novoda/noplayer/text/NoPlayerSubtitleDecoderFactory.java",
    "chars": 3265,
    "preview": "package com.novoda.noplayer.text;\n\nimport com.google.android.exoplayer2.Format;\nimport com.google.android.exoplayer2.tex"
  },
  {
    "path": "core/src/main/res/layout/noplayer_view.xml",
    "chars": 855,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.exoplayer2.ui.AspectRatioFrameLayout xmlns:android=\"http://sc"
  },
  {
    "path": "core/src/test/java/com/google/android/exoplayer2/ExoPlaybackExceptionFactory.java",
    "chars": 250,
    "preview": "package com.google.android.exoplayer2;\n\npublic class ExoPlaybackExceptionFactory {\n\n    public static ExoPlaybackExcepti"
  },
  {
    "path": "core/src/test/java/com/google/android/exoplayer2/drm/FrameworkMediaCryptoFixture.java",
    "chars": 1151,
    "preview": "package com.google.android.exoplayer2.drm;\n\nimport android.media.MediaCrypto;\nimport android.media.MediaCryptoException;"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/LoadTimeoutTest.java",
    "chars": 2399,
    "preview": "package com.novoda.noplayer;\n\nimport android.os.Handler;\n\nimport com.novoda.noplayer.internal.Clock;\nimport com.novoda.n"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/NoPlayerCreatorTest.java",
    "chars": 5909,
    "preview": "package com.novoda.noplayer;\n\nimport android.content.Context;\n\nimport com.novoda.noplayer.drm.DownloadedModularDrm;\nimpo"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/PlayerSurfaceHolderTest.java",
    "chars": 2594,
    "preview": "package com.novoda.noplayer;\n\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceView;\nimport android.view.Te"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/PlayerTypeTest.java",
    "chars": 439,
    "preview": "package com.novoda.noplayer;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\np"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/HeartTest.java",
    "chars": 3078,
    "preview": "package com.novoda.noplayer.internal;\n\nimport android.os.Handler;\n\nimport com.novoda.noplayer.NoPlayer;\n\nimport org.juni"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/drm/provision/HttpPostingProvisionExecutorTest.java",
    "chars": 2443,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\nimport com.novoda.noplayer.drm.ModularDrmProvisionRequest;\n\nimport "
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/drm/provision/ProvisioningCapabilitiesFixtures.java",
    "chars": 655,
    "preview": "package com.novoda.noplayer.internal.drm.provision;\n\nimport android.os.Build;\n\npublic final class ProvisioningCapabiliti"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerFacadeTest.java",
    "chars": 22563,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.net.Uri;\nimport android.view.SurfaceHolder;\nimport andro"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerInformationTest.java",
    "chars": 1057,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.ExoPlayerLibraryInfo;\nimport com.n"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/ExoPlayerTwoImplTest.java",
    "chars": 23366,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.net.Uri;\nimport android.view.View;\nimport com.google.and"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreatorTest.java",
    "chars": 1411,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport android.content.Context;\n\nimport com.novoda.noplayer.internal.ex"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/PlayerSubtitleTrackFixture.java",
    "chars": 1685,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.novoda.noplayer.model.PlayerSubtitleTrack;\n\nclass PlayerSubt"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/SecurityDowngradingCodecSelectorTest.java",
    "chars": 2483,
    "preview": "package com.novoda.noplayer.internal.exoplayer;\n\nimport com.google.android.exoplayer2.mediacodec.MediaCodecUtil;\n\nimport"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/drm/DrmSessionCreatorFactoryTest.java",
    "chars": 4522,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.os.Handler;\n\nimport com.novoda.noplayer.UnableToCrea"
  },
  {
    "path": "core/src/test/java/com/novoda/noplayer/internal/exoplayer/drm/LocalDrmSessionManagerTest.java",
    "chars": 6148,
    "preview": "package com.novoda.noplayer.internal.exoplayer.drm;\n\nimport android.media.MediaCryptoException;\nimport android.media.Med"
  }
]

// ... and 66 more files (download for full content)

About this extraction

This page contains the full source code of the novoda/no-player GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 266 files (738.5 KB), approximately 169.2k tokens, and a symbol index with 1925 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!