roly151
roly151

Reputation: 88

Android TV - Exoplayer controls show on emulator but not on TV

I'm a relative beginner. I've created a simple exoplayer activity to play an HLS stream on Android TV. It loads and plays fine, and whilst the controls are showing, I can pause, play, rewind, fast forward fine. On the android studio emulator (Android TV API 28), once the controls hide themselves, I can click on the emulator and the controls unhide, so it all works as expected.

On my device (Nvidia Shield), it all works fine until the controls hide. Clicking on the remote does not get the playback controls to show again. I have been looking to find the solution and I found a few mentions about glue etc, but these pages provide parts of the solution. I have not found any tutorials about how to implement exoplayer on Android TV. And since I'm a beginner, I could really do with a guide. All the guides I can find are about implementing exoplayer in Android - which is how I got to the below. The only guide I found that made sense for me was this one (https://rileymacdonald.ca/2017/09/21/android-tv-how-to-add-leanback-media-controls-to-google-exoplayer/) - but it's old and doesnt work. And I'm not sure if I have to implement the leanback controls, or can just use the ones in exoplayer? An updated guide or help with my issue of the controls not re-showing when the remote is clicked would be much appreciated.

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.exoplayertest">

    <uses-feature
        android:name="android.software.leanback"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.touchscreen"
        android:required="false" />

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:banner="@drawable/tv">
        <activity android:name=".MainActivity"
            android:configChanges="keyboard|keyboardHidden|navigation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/flRoot"
        android:focusable="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:keepScreenOn="true">
    
        <com.google.android.exoplayer2.ui.PlayerView
            android:id="@+id/playerView"
            android:focusable="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <ProgressBar
                android:id="@+id/progressBar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:visibility="gone"/>
    
        </com.google.android.exoplayer2.ui.PlayerView>

</FrameLayout>  

MainActivity.kt

class MainActivity : Activity() {

    private var mPlayer: SimpleExoPlayer? = null
    private val hlsUrl = "https://9now-livestreams.akamaized.net/hls/live/2010628/rush-mel/master.m3u8"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }

    private fun initPlayer() {
        mPlayer = SimpleExoPlayer.Builder(this).build()
        // Bind the player to the view.
        playerView.player = mPlayer
        mPlayer!!.playWhenReady = true
        //mPlayer!!.seekTo(playbackPosition)
        mPlayer!!.prepare(buildMediaSource(), false, false)

    }

    override fun onStart() {
        super.onStart()

        initPlayer()
    }

    override fun onResume() {
        super.onResume()
        hideSystemUi()
        if (mPlayer == null) {
            initPlayer()
        }
    }

    override fun onPause() {
        super.onPause()
    }

    override fun onStop() {
        super.onStop()

        releasePlayer()
    }

    @SuppressLint("InlinedApi")
    private fun hideSystemUi() {
        playerView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
                or View.SYSTEM_UI_FLAG_FULLSCREEN
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
    }

    private fun releasePlayer() {
        if (mPlayer == null) {
            return
        }
        mPlayer!!.release()
        mPlayer = null
    }

    private fun buildMediaSource(): MediaSource {
        val userAgent =
            Util.getUserAgent(playerView.context, playerView.context.getString(R.string.app_name))

        val dataSourceFactory = DefaultHttpDataSourceFactory(userAgent)
        val hlsMediaSource =
            HlsMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(hlsUrl))

        return hlsMediaSource
    }
}

Upvotes: 1

Views: 2163

Answers (1)

roly151
roly151

Reputation: 88

I'm not sure why I had the problem (or even if it is normal behaviour), but if anyone has a similar problem, I added the following to my PlayerActivity class and it now works.

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    when (keyCode) {
        KeyEvent.KEYCODE_DPAD_CENTER -> {
            if (!playerView.isControllerVisible()) {
                playerView.showController()
            }
            return true
        }
    }
    return super.onKeyDown(keyCode, event)
}

Upvotes: 2

Related Questions