Handle Rotation with ExoPlayer in Jetpack Compose

Julien sez'n
2 min readJun 15, 2022

Give a try on Jetpack Compose integration, found that wonderfull to eliminate XML and have all UI in Kotlin, like SwiftUI.

I will not give here the instructions for integrate Exo, but you can find source at https://github.com/sezn/VideoPlayer

So, the goal is when you create your Scaffold, you need to handle configuration change to hide or display Top & Bottom Bar etc.

First to do:

configure Activity in Manifest file for handle screenOrientation & configChanges (the screenSize will help if you integrate PIP after):

<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:configChanges="orientation|screenSize"
android:screenOrientation="sensor"
android:theme="@style/Theme.VideoPlayer">

So, here is main Composable (the canPop is just for Test navigation 😐)

@Composable
fun CrossApp() {
val navController = rememberNavController()
var (canPop, setCanPop) = remember { mutableStateOf(false) }

val listener = NavController.OnDestinationChangedListener { controller, _, _ ->
setCanPop(controller.previousBackStackEntry != null)
}
navController.addOnDestinationChangedListener(listener)
val conf = LocalConfiguration.current

AppTheme {
Scaffold(
topBar = {
// Here, we take care of Orientation to display TopAppBar
if(conf.orientation == Configuration.ORIENTATION_PORTRAIT)
TopAppBar(
title = { Text(stringResource(id = R.string.app_name), color = Color.White) },
backgroundColor = MaterialTheme.colors.onBackground,
navigationIcon = {
if (canPop) {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(imageVector = Icons.Filled.ArrowBack, null, tint = Color.White)
}
} else null
}
)
// Here, nothing, you can return null like in comment, but no need, no Component to Render :p
// else null
},
content = { NavigationHost(navController = navController) },
bottomBar = {
if(conf.orientation == Configuration.ORIENTATION_PORTRAIT)
BottomNavigationBar(navController = navController)
// No else :p
}
)
}
}

So cool, now in Activity, just override screenOrientation, then add both methods for hide or show controls & status bar (like before with android )

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CrossApp()
}
}

override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
hideSystemUI()
else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
showSystemUI()
}

private fun showSystemUI() {
WindowCompat.setDecorFitsSystemWindows(window, true)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.decorView.apply {
systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
}
} else {
window.insetsController?.apply {
show(WindowInsets.Type.systemBars())
systemBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT
}
}
}

fun hideSystemUI() {
//Hide the status bars
WindowCompat.setDecorFitsSystemWindows(window, false)

WindowInsetsControllerCompat(window,
window.decorView).hide(WindowInsetsCompat.Type.systemBars())
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.decorView.apply {
systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN
}
} else {
window.insetsController?.apply {
hide(WindowInsets.Type.systemBars())
systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}
}

Now, you should have a Video Player wich show Top & Bottom Bars in Portrait, and is FullScreen without Bars in Landscape.

--

--