Building a modern Android video player experience similar to YouTube requires several components working together:
- Detecting screen orientation changes
- Handling fullscreen transitions
- Listening for device orientation events
- Rotating views programmatically
- Managing
PlayerViewoverlay interactions - Supporting delayed orientation changes
- Implementing ExoPlayer/Media3 fullscreen behavior
This guide covers the key concepts and Kotlin examples for creating a responsive Android video player.
Understanding Screen Orientation vs Device Orientation
Many Android developers confuse these concepts:
Screen Orientation
The current layout orientation used by Android:
val orientation = resources.configuration.orientation
when (orientation) {
Configuration.ORIENTATION_PORTRAIT -> {
// Portrait UI
}
Configuration.ORIENTATION_LANDSCAPE -> {
// Landscape UI
}
}
Device Orientation
The physical position of the device measured by sensors.
Examples:
- Upright portrait
- Upside-down portrait
- Landscape left
- Landscape right
- Flat on a table
This is detected using sensors or an OrientationEventListener.
Listening for Device Orientation Changes
The simplest approach:
private lateinit var orientationListener: OrientationEventListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
orientationListener = object : OrientationEventListener(this) {
override fun onOrientationChanged(orientation: Int) {
when {
orientation in 45..135 -> {
// Landscape right
}
orientation in 225..315 -> {
// Landscape left
}
orientation >= 315 || orientation <= 45 -> {
// Portrait
}
}
}
}
}
override fun onResume() {
super.onResume()
orientationListener.enable()
}
override fun onPause() {
super.onPause()
orientationListener.disable()
}
The orientation value ranges from:
0° = Portrait
90° = Landscape
180° = Reverse Portrait
270° = Reverse Landscape
Adding Delayed Orientation Changes
YouTube does not instantly rotate the UI the moment the sensor changes.
A delayed approach avoids accidental fullscreen transitions:
private val handler = Handler(Looper.getMainLooper())
private var orientationRunnable: Runnable? = null
private fun scheduleOrientationChange(action: () -> Unit) {
orientationRunnable?.let {
handler.removeCallbacks(it)
}
orientationRunnable = Runnable {
action()
}
handler.postDelayed(
orientationRunnable!!,
500
)
}
Usage:
scheduleOrientationChange {
enterFullscreen()
}
This creates a smoother experience similar to YouTube.
Detecting Screen Orientation Changes
Inside an Activity:
override fun onConfigurationChanged(
newConfig: Configuration
) {
super.onConfigurationChanged(newConfig)
when (newConfig.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
enterFullscreen()
}
Configuration.ORIENTATION_PORTRAIT -> {
exitFullscreen()
}
}
}
Manifest:
<activity
android:name=".PlayerActivity"
android:configChanges="orientation|screenSize" />
Fullscreen Implementation
Enter Fullscreen
private fun enterFullscreen() {
requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
WindowCompat.setDecorFitsSystemWindows(
window,
false
)
WindowInsetsControllerCompat(
window,
window.decorView
).hide(WindowInsetsCompat.Type.systemBars())
}
Exit Fullscreen
private fun exitFullscreen() {
requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
WindowCompat.setDecorFitsSystemWindows(
window,
true
)
WindowInsetsControllerCompat(
window,
window.decorView
).show(WindowInsetsCompat.Type.systemBars())
}
Programmatically Rotating a View
Any view, including a CardView, can be rotated:
cardView.rotation = 90f
Animated rotation:
cardView.animate()
.rotation(90f)
.setDuration(300)
.start()
PlayerView Overlay Click Handling
A common pattern is placing an overlay above the player.
Layout
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.media3.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<View
android:id="@+id/playerOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
Click Listener
binding.playerOverlay.setOnClickListener {
if (binding.playerView.isControllerFullyVisible) {
binding.playerView.hideController()
} else {
binding.playerView.showController()
}
}
This mimics YouTube’s tap-to-show-controls behavior.
YouTube-Like Fullscreen Behavior
YouTube generally follows this pattern:
Portrait
- Video displayed inline
- Comments visible below
- Normal activity layout
Rotate to Landscape
- Sensor detects orientation
- Short delay applied
- Video enters fullscreen
- System bars hidden
- Controller shown briefly
Rotate Back
- Exit fullscreen
- Restore portrait layout
- Show system bars
Pseudo-flow:
OrientationEventListener
↓
Delay 300-500ms
↓
Landscape?
↓
Enter Fullscreen
↓
Hide System Bars
↓
Show Controls
Recommended Modern Stack
For Android development in 2026:
- Media3 (
androidx.media3) PlayerViewOrientationEventListenerWindowInsetsControllerCompatViewBindingLifecycle-aware player management
Avoid:
- Deprecated Orientation Sensor APIs
- Legacy ExoPlayer 2 packages when Media3 is available
- Old fullscreen flags like
FLAG_FULLSCREEN
Example Architecture
PlayerFragment
│
├── PlayerView
├── Overlay View
├── OrientationEventListener
├── Fullscreen Manager
└── Media3 ExoPlayer
This architecture provides behavior very close to YouTube’s fullscreen experience while remaining clean, maintainable, and compatible with modern Android development practices.


