Glimpse: Rework media player

We now have fullscreen mode!

Change-Id: Iad8b93145c79413473d20fa510e28ec608a44e38
This commit is contained in:
Sebastiano Barezzi 2023-08-27 04:14:33 +02:00
parent 74389a9725
commit 39c039e38d
No known key found for this signature in database
GPG Key ID: 763BD3AE91A7A13F
15 changed files with 431 additions and 41 deletions

View File

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package androidx.media3.ui
@androidx.media3.common.util.UnstableApi
fun PlayerControlView.updateAll() {
updateAll()
}
@androidx.media3.common.util.UnstableApi
fun PlayerControlView.requestPlayPauseFocus() {
requestPlayPauseFocus()
}

View File

@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.glimpse.ext
import android.content.res.Configuration
import android.os.Build
val Configuration.isNightMode
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
isNightModeActive
} else when (uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
Configuration.UI_MODE_NIGHT_UNDEFINED -> null
Configuration.UI_MODE_NIGHT_NO -> false
Configuration.UI_MODE_NIGHT_YES -> true
else -> null
}

View File

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.glimpse.ext
import android.view.View
import androidx.media3.ui.PlayerControlView
import androidx.media3.ui.requestPlayPauseFocus
import androidx.media3.ui.updateAll
@androidx.media3.common.util.UnstableApi
fun PlayerControlView.fade(visible: Boolean) {
(this as View).fade(visible)
// This is needed to resume progress updating
if (visible) {
updateAll()
requestPlayPauseFocus()
show()
}
}

View File

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.glimpse.ext
import android.view.View
import androidx.core.view.isVisible
/**
* Get the system's default short animation time.
*/
val View.shortAnimTime
get() = resources.getInteger(android.R.integer.config_shortAnimTime)
/**
* Update the [View]'s visibility using a fade animation.
* @param visible Whether the view should be visible or not.
*/
fun View.fade(visible: Boolean) {
with(animate()) {
cancel()
if (visible && !isVisible) {
isVisible = true
}
alpha(
when (visible) {
true -> 1f
false -> 0f
}
)
duration = shortAnimTime.toLong()
setListener(null)
withEndAction {
isVisible = visible
}
}
}

View File

@ -0,0 +1,61 @@
/*
* SPDX-FileCopyrightText: 2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.glimpse.ext
import android.view.Window
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
private val Window.windowInsetsController
get() = WindowInsetsControllerCompat(this, decorView)
var Window.isAppearanceLightStatusBars
get() = windowInsetsController.isAppearanceLightStatusBars
set(value) {
windowInsetsController.isAppearanceLightStatusBars = value
}
fun Window.resetStatusBarAppearance() {
windowInsetsController.isAppearanceLightStatusBars =
context.resources.configuration.isNightMode != true
}
fun Window.setBarsVisibility(
systemBars: Boolean? = null,
statusBars: Boolean? = null,
navigationBars: Boolean? = null,
) {
// Configure the behavior of the hidden bars
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
val systemBarsType = WindowInsetsCompat.Type.systemBars()
val statusBarsType = WindowInsetsCompat.Type.statusBars()
val navigationBarsType = WindowInsetsCompat.Type.navigationBars()
// Set the system bars visibility
systemBars?.let {
when (it) {
true -> windowInsetsController.show(systemBarsType)
false -> windowInsetsController.hide(systemBarsType)
}
}
// Set the status bars visibility
statusBars?.let {
when (it) {
true -> windowInsetsController.show(statusBarsType)
false -> windowInsetsController.hide(statusBarsType)
}
}
// Set the navigation bars visibility
navigationBars?.let {
when (it) {
true -> windowInsetsController.show(navigationBarsType)
false -> windowInsetsController.hide(navigationBarsType)
}
}
}

View File

@ -7,10 +7,11 @@ package org.lineageos.glimpse.fragments
import android.app.Activity
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.View.MeasureSpec
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.TextView
@ -19,14 +20,16 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.os.bundleOf
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import androidx.navigation.fragment.findNavController
import androidx.viewpager2.widget.ViewPager2
@ -43,7 +46,7 @@ import org.lineageos.glimpse.models.MediaType
import org.lineageos.glimpse.recyclerview.MediaViewerAdapter
import org.lineageos.glimpse.ui.MediaInfoBottomSheetDialog
import org.lineageos.glimpse.utils.PermissionsGatedCallback
import org.lineageos.glimpse.viewmodels.MediaViewModel
import org.lineageos.glimpse.viewmodels.MediaViewerViewModel
import java.text.SimpleDateFormat
/**
@ -51,9 +54,10 @@ import java.text.SimpleDateFormat
* Use the [MediaViewerFragment.newInstance] factory method to
* create an instance of this fragment.
*/
@androidx.media3.common.util.UnstableApi
class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
// View models
private val mediaViewModel: MediaViewModel by viewModels { MediaViewModel.Factory }
private val mediaViewModel: MediaViewerViewModel by viewModels { MediaViewerViewModel.Factory }
// Views
private val adjustButton by getViewProperty<ImageButton>(R.id.adjustButton)
@ -81,9 +85,19 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
}
// Player
private val exoPlayerListener = object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
view?.keepScreenOn = isPlaying
}
}
private val exoPlayerLazy = lazy {
ExoPlayer.Builder(requireContext()).build().apply {
repeatMode = ExoPlayer.REPEAT_MODE_ONE
addListener(exoPlayerListener)
}
}
private val exoPlayer
@ -95,7 +109,7 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
// Adapter
private val mediaViewerAdapter by lazy {
MediaViewerAdapter(exoPlayerLazy, mediaViewModel.mediaPositionLiveData)
MediaViewerAdapter(exoPlayerLazy, mediaViewModel)
}
// Arguments
@ -208,6 +222,9 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
super.onResume()
exoPlayer?.play()
// Force status bar icons to be light
requireActivity().window.isAppearanceLightStatusBars = false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -270,15 +287,22 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
topSheetConstraintLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.left
rightMargin = insets.right
topMargin = insets.top
}
bottomSheetLinearLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = insets.bottom
leftMargin = insets.left
rightMargin = insets.right
// Avoid updating the sheets height when they're hidden.
// Once the system bars will be made visible again, this function
// will be called again.
if (mediaViewModel.fullscreenModeLiveData.value != true) {
topSheetConstraintLayout.updatePadding(
left = insets.left,
right = insets.right,
top = insets.top,
)
bottomSheetLinearLayout.updatePadding(
bottom = insets.bottom,
left = insets.left,
right = insets.right,
)
updateSheetsHeight()
}
windowInsets
@ -310,6 +334,22 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
}
}
view.findViewTreeLifecycleOwner()?.let {
mediaViewModel.fullscreenModeLiveData.observe(it) { fullscreenMode ->
topSheetConstraintLayout.fade(!fullscreenMode)
bottomSheetLinearLayout.fade(!fullscreenMode)
requireActivity().window.setBarsVisibility(systemBars = !fullscreenMode)
// If the sheets are being made visible again, update the values
if (!fullscreenMode) {
updateSheetsHeight()
}
}
}
updateSheetsHeight()
permissionsGatedCallback.runAfterPermissionsCheck()
}
@ -325,6 +365,12 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
super.onPause()
exoPlayer?.pause()
// Restore status bar icons appearance
requireActivity().window.resetStatusBarAppearance()
// Restore system bars visibility
requireActivity().window.setBarsVisibility(systemBars = true)
}
override fun onDestroy() {
@ -333,6 +379,12 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
super.onDestroy()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
updateSheetsHeight()
}
private fun initData(data: List<Media>) {
mediaViewerAdapter.data = data.toTypedArray()
viewPager.setCurrentItem(mediaViewModel.mediaPosition, false)
@ -359,6 +411,16 @@ class MediaViewerFragment : Fragment(R.layout.fragment_media_viewer) {
}
}
private fun updateSheetsHeight() {
topSheetConstraintLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
bottomSheetLinearLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
mediaViewModel.sheetsHeightLiveData.value = Pair(
topSheetConstraintLayout.measuredHeight,
bottomSheetLinearLayout.measuredHeight,
)
}
companion object {
private const val KEY_ALBUM = "album"
private const val KEY_MEDIA = "media"

View File

@ -10,19 +10,23 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.core.view.isVisible
import androidx.lifecycle.LiveData
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerControlView
import androidx.media3.ui.PlayerView
import androidx.recyclerview.widget.RecyclerView
import coil.load
import org.lineageos.glimpse.R
import org.lineageos.glimpse.ext.fade
import org.lineageos.glimpse.models.Media
import org.lineageos.glimpse.models.MediaType
import org.lineageos.glimpse.viewmodels.MediaViewerViewModel
@androidx.media3.common.util.UnstableApi
class MediaViewerAdapter(
private val exoPlayer: Lazy<ExoPlayer>,
private val currentPositionLiveData: LiveData<Int>,
private val mediaViewerViewModel: MediaViewerViewModel,
) : RecyclerView.Adapter<MediaViewerAdapter.MediaViewHolder>() {
var data: Array<Media> = arrayOf()
set(value) {
@ -47,7 +51,7 @@ class MediaViewerAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MediaViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.media_view, parent, false),
exoPlayer, currentPositionLiveData,
exoPlayer, mediaViewerViewModel
)
override fun onBindViewHolder(holder: MediaViewHolder, position: Int) {
@ -69,25 +73,62 @@ class MediaViewerAdapter(
class MediaViewHolder(
private val view: View,
private val exoPlayer: Lazy<ExoPlayer>,
private val currentPositionLiveData: LiveData<Int>,
private val mediaViewerViewModel: MediaViewerViewModel,
) : RecyclerView.ViewHolder(view) {
// Views
private val imageView = view.findViewById<ImageView>(R.id.imageView)
private val playerControlView = view.findViewById<PlayerControlView>(R.id.exo_controller)
private val playerView = view.findViewById<PlayerView>(R.id.playerView)
private lateinit var media: Media
private var position = -1
private val observer = { currentPosition: Int ->
private val mediaPositionObserver = { currentPosition: Int ->
val isNowVideoPlayer = currentPosition == position && media.mediaType == MediaType.VIDEO
imageView.isVisible = !isNowVideoPlayer
playerView.isVisible = isNowVideoPlayer
playerView.player = when (isNowVideoPlayer) {
if (!isNowVideoPlayer || mediaViewerViewModel.fullscreenModeLiveData.value == true) {
playerControlView.hideImmediately()
} else {
playerControlView.show()
}
val player = when (isNowVideoPlayer) {
true -> exoPlayer.value
false -> null
}
playerView.player = player
playerControlView.player = player
}
private val sheetsHeightObserver = { sheetsHeight: Pair<Int, Int> ->
if (mediaViewerViewModel.fullscreenModeLiveData.value != true) {
val (topHeight, bottomHeight) = sheetsHeight
// Place the player controls between the two sheets
playerControlView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = topHeight
bottomMargin = bottomHeight
}
}
}
private val fullscreenModeObserver = { fullscreenMode: Boolean ->
if (media.mediaType == MediaType.VIDEO) {
playerControlView.fade(!fullscreenMode)
}
}
init {
imageView.setOnClickListener {
mediaViewerViewModel.toggleFullscreenMode()
}
playerView.setOnClickListener {
mediaViewerViewModel.toggleFullscreenMode()
}
}
fun bind(media: Media, position: Int) {
@ -101,12 +142,16 @@ class MediaViewerAdapter(
fun onViewAttachedToWindow() {
view.findViewTreeLifecycleOwner()?.let {
currentPositionLiveData.observe(it, observer)
mediaViewerViewModel.mediaPositionLiveData.observe(it, mediaPositionObserver)
mediaViewerViewModel.sheetsHeightLiveData.observe(it, sheetsHeightObserver)
mediaViewerViewModel.fullscreenModeLiveData.observe(it, fullscreenModeObserver)
}
}
fun onViewDetachedFromWindow() {
currentPositionLiveData.removeObserver(observer)
mediaViewerViewModel.mediaPositionLiveData.removeObserver(mediaPositionObserver)
mediaViewerViewModel.sheetsHeightLiveData.removeObserver(sheetsHeightObserver)
mediaViewerViewModel.fullscreenModeLiveData.removeObserver(fullscreenModeObserver)
}
}
}

View File

@ -19,7 +19,7 @@ import kotlinx.coroutines.flow.flatMapLatest
import org.lineageos.glimpse.GlimpseApplication
import org.lineageos.glimpse.repository.MediaRepository
class MediaViewModel(
open class MediaViewModel(
private val savedStateHandle: SavedStateHandle, private val mediaRepository: MediaRepository
) : ViewModel() {
private val mediaPositionInternal = savedStateHandle.getLiveData<Int>(MEDIA_POSITION_KEY)

View File

@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.glimpse.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import org.lineageos.glimpse.GlimpseApplication
import org.lineageos.glimpse.repository.MediaRepository
class MediaViewerViewModel(
savedStateHandle: SavedStateHandle,
mediaRepository: MediaRepository,
) : MediaViewModel(savedStateHandle, mediaRepository) {
/**
* The current height of top and bottom sheets, used to apply padding to media view UI.
*/
val sheetsHeightLiveData = MutableLiveData<Pair<Int, Int>>()
/**
* Fullscreen mode, set by the user with a single tap on the viewed media.
*/
val fullscreenModeLiveData = MutableLiveData(false)
/**
* Toggle fullscreen mode.
*/
fun toggleFullscreenMode() {
fullscreenModeLiveData.value = when (fullscreenModeLiveData.value) {
true -> false
else -> true
}
}
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
MediaViewerViewModel(
savedStateHandle = createSavedStateHandle(),
mediaRepository = (this[APPLICATION_KEY] as GlimpseApplication).mediaRepository,
)
}
}
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:angle="270"
android:endColor="@android:color/black"
android:startColor="@android:color/transparent"
android:type="linear" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:angle="270"
android:endColor="@android:color/transparent"
android:startColor="@android:color/black"
android:type="linear" />
</shape>
</item>
</selector>

View File

@ -8,14 +8,24 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:theme="@style/Theme.Glimpse.MediaViewer"
tools:context=".fragments.MediaViewerFragment">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topSheetConstraintLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="24dp"
android:background="@drawable/bg_media_viewer_top_sheet"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@ -26,6 +36,7 @@
android:layout_height="24dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_marginStart="16dp"
android:backgroundTint="@android:color/transparent"
android:src="@drawable/ic_back"
app:layout_constraintBottom_toBottomOf="parent"
@ -38,6 +49,7 @@
style="@style/Theme.Glimpse.MediaViewer.DateTimeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginTop="16dp"
android:textAllCaps="true"
android:textColor="?attr/colorOnSurface"
@ -50,6 +62,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="24dp"
android:textAllCaps="true"
android:textColor="?attr/colorOnSurface"
app:layout_constraintBottom_toBottomOf="parent"
@ -57,19 +70,11 @@
app:layout_constraintTop_toBottomOf="@+id/dateTextView" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottomSheetLinearLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/topSheetConstraintLayout" />
<LinearLayout
android:id="@+id/bottomSheetLinearLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_media_viewer_bottom_sheet"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -6,8 +6,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
android:layout_height="match_parent">
<!-- Images -->
<ImageView
@ -29,6 +28,25 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
app:show_timeout="0"
app:use_controller="false">
<androidx.media3.ui.PlayerControlView
android:id="@+id/exo_controller"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/transparent"
android:backgroundTint="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:show_next_button="false"
app:show_previous_button="false"
app:show_shuffle_button="false"
app:show_subtitle_button="true" />
</androidx.media3.ui.PlayerView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Media3: Remove color overlay on visible player controls -->
<color name="exo_bottom_bar_background" tools:override="true">@android:color/transparent</color>
<color name="exo_black_opacity_60" tools:override="true">@android:color/transparent</color>
</resources>

View File

@ -3,12 +3,15 @@
SPDX-FileCopyrightText: 2023 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
-->
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme -->
<style name="Theme.Glimpse" parent="Theme.Material3.DayNight.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">
shortEdges
</item>
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
</style>
<!-- Collapsing toolbar style -->
@ -34,7 +37,7 @@
</style>
<!-- Media viewer -->
<style name="Theme.Glimpse.MediaViewer" />
<style name="Theme.Glimpse.MediaViewer" parent="Theme.Material3.Dark.NoActionBar" />
<!-- Media viewer top sheet -->
<style name="Theme.Glimpse.MediaViewer.TopSheet" />