Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add video zoom options and dialog #505

Merged
merged 6 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data class PlayerPreferences(
val rememberSelections: Boolean = true,
val preferredAudioLanguage: String = "",
val playerScreenOrientation: ScreenOrientation = ScreenOrientation.VIDEO_ORIENTATION,
val playerVideoZoom: VideoZoom = VideoZoom.BEST_FIT,
val defaultPlaybackSpeed: Float = 1.0f,
val controllerAutoHideTimeout: Int = 2,
val seekIncrement: Int = 10,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dev.anilbeesetti.nextplayer.core.model

enum class VideoZoom {
BEST_FIT, STRETCH, CROP, HUNDRED_PERCENT
}
5 changes: 5 additions & 0 deletions core/ui/src/main/res/drawable/ic_crop_landscape.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,5L5,5c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM18,17L6,17c-0.55,0 -1,-0.45 -1,-1L5,8c0,-0.55 0.45,-1 1,-1h12c0.55,0 1,0.45 1,1v8c0,0.55 -0.45,1 -1,1z"/>
</vector>
5 changes: 5 additions & 0 deletions core/ui/src/main/res/drawable/ic_fit_screen.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M18,4h2c1.1,0 2,0.9 2,2v2c0,0.55 -0.45,1 -1,1h0c-0.55,0 -1,-0.45 -1,-1V6h-2c-0.55,0 -1,-0.45 -1,-1v0C17,4.45 17.45,4 18,4zM4,8l0,-2h2c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1H4C2.9,4 2,4.9 2,6l0,2c0,0.55 0.45,1 1,1h0C3.55,9 4,8.55 4,8zM20,16v2h-2c-0.55,0 -1,0.45 -1,1v0c0,0.55 0.45,1 1,1h2c1.1,0 2,-0.9 2,-2v-2c0,-0.55 -0.45,-1 -1,-1h0C20.45,15 20,15.45 20,16zM6,18H4v-2c0,-0.55 -0.45,-1 -1,-1h0c-0.55,0 -1,0.45 -1,1v2c0,1.1 0.9,2 2,2h2c0.55,0 1,-0.45 1,-1v0C7,18.45 6.55,18 6,18zM16,8H8c-1.1,0 -2,0.9 -2,2v4c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2v-4C18,8.9 17.1,8 16,8z"/>
</vector>
5 changes: 5 additions & 0 deletions core/ui/src/main/res/drawable/ic_width_wide.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,4H4C2.9,4 2,4.9 2,6v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.1,4 20,4zM4,6h2v12H4V6zM20,18h-2V6h2V18z"/>
</vector>
6 changes: 5 additions & 1 deletion core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
<string name="smallest">Smallest</string>
<string name="largest">Largest</string>
<string name="dark_theme">Dark theme</string>
<string name="video_zoom">Video Zoom</string>
<string name="video_zoom">Video zoom</string>
<string name="dynamic_theme">Dynamic color theme</string>
<string name="dynamic_theme_description">Apply colors from system wallpaper to the application theme</string>
<string name="controls_lock">Lock controls</string>
Expand Down Expand Up @@ -141,4 +141,8 @@
<string name="delete_file">The following file will be deleted permanently</string>
<string name="subtitle_text_encoding">Subtitle text encoding</string>
<string name="delete_folder">All video files in the following folder will be deleted permanently.</string>
<string name="best_fit">Best fit</string>
<string name="stretch">Stretch</string>
<string name="crop">Crop</string>
<string name="hundred_percent">100%</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ import dev.anilbeesetti.nextplayer.core.common.extensions.getPath
import dev.anilbeesetti.nextplayer.core.model.DecoderPriority
import dev.anilbeesetti.nextplayer.core.model.ScreenOrientation
import dev.anilbeesetti.nextplayer.core.model.ThemeConfig
import dev.anilbeesetti.nextplayer.core.model.VideoZoom
import dev.anilbeesetti.nextplayer.core.ui.R as coreUiR
import dev.anilbeesetti.nextplayer.feature.player.databinding.ActivityPlayerBinding
import dev.anilbeesetti.nextplayer.feature.player.dialogs.PlaybackSpeedControlsDialogFragment
import dev.anilbeesetti.nextplayer.feature.player.dialogs.TrackSelectionDialogFragment
import dev.anilbeesetti.nextplayer.feature.player.dialogs.VideoZoomOptionsDialogFragment
import dev.anilbeesetti.nextplayer.feature.player.dialogs.getCurrentTrackIndex
import dev.anilbeesetti.nextplayer.feature.player.extensions.audioSessionId
import dev.anilbeesetti.nextplayer.feature.player.extensions.getLocalSubtitles
Expand Down Expand Up @@ -106,6 +108,7 @@ class PlayerActivity : AppCompatActivity() {
private var isPlayingOnScrubStart: Boolean = false
private var currentOrientation: Int? = null
private var currentVideoOrientation: Int? = null
private var currentVideoSize: VideoSize? = null

private val shouldFastSeek: Boolean
get() = playerPreferences.shouldFastSeek(player.duration)
Expand Down Expand Up @@ -138,6 +141,7 @@ class PlayerActivity : AppCompatActivity() {
*/
private lateinit var audioTrackButton: ImageButton
private lateinit var backButton: ImageButton
private lateinit var exoContentFrameLayout: AspectRatioFrameLayout
private lateinit var lockControlsButton: ImageButton
private lateinit var nextButton: ImageButton
private lateinit var playbackSpeedButton: ImageButton
Expand Down Expand Up @@ -181,6 +185,7 @@ class PlayerActivity : AppCompatActivity() {
// Initializing views
audioTrackButton = binding.playerView.findViewById(R.id.btn_audio_track)
backButton = binding.playerView.findViewById(R.id.back_button)
exoContentFrameLayout = binding.playerView.findViewById(R.id.exo_content_frame)
lockControlsButton = binding.playerView.findViewById(R.id.btn_lock_controls)
nextButton = binding.playerView.findViewById(R.id.btn_play_next)
playbackSpeedButton = binding.playerView.findViewById(R.id.btn_playback_speed)
Expand Down Expand Up @@ -370,14 +375,6 @@ class PlayerActivity : AppCompatActivity() {
playVideo(playlistManager.getPrev()!!)
}
}
videoZoomButton.setOnClickListener {
binding.playerView.resizeMode =
if (binding.playerView.resizeMode != AspectRatioFrameLayout.RESIZE_MODE_ZOOM) {
AspectRatioFrameLayout.RESIZE_MODE_ZOOM
} else {
AspectRatioFrameLayout.RESIZE_MODE_FIT
}
}
lockControlsButton.setOnClickListener {
playerUnlockControls.visibility = View.INVISIBLE
playerLockControls.visibility = View.VISIBLE
Expand All @@ -390,6 +387,18 @@ class PlayerActivity : AppCompatActivity() {
isControlsLocked = false
toggleSystemBars(showBars = true)
}
videoZoomButton.setOnClickListener {
val videoZoom = playerPreferences.playerVideoZoom.next()
applyVideoZoom(videoZoom)
}

videoZoomButton.setOnLongClickListener {
VideoZoomOptionsDialogFragment(
currentVideoZoom = playerPreferences.playerVideoZoom,
onVideoZoomOptionSelected = { applyVideoZoom(it) }
).show(supportFragmentManager, "VideoZoomOptionsDialog")
true
}
screenRotationButton.setOnClickListener {
requestedOrientation = when (resources.configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
Expand Down Expand Up @@ -485,6 +494,9 @@ class PlayerActivity : AppCompatActivity() {

@SuppressLint("SourceLockedOrientationActivity")
override fun onVideoSizeChanged(videoSize: VideoSize) {
currentVideoSize = videoSize
applyVideoZoom(playerPreferences.playerVideoZoom)

if (currentOrientation != null) return

if (playerPreferences.playerScreenOrientation == ScreenOrientation.VIDEO_ORIENTATION) {
Expand Down Expand Up @@ -659,6 +671,45 @@ class PlayerActivity : AppCompatActivity() {
}.build()
}
}

private fun resetExoContentFrameWidthAndHeight() {
exoContentFrameLayout.layoutParams.width = binding.playerView.width
exoContentFrameLayout.layoutParams.height = binding.playerView.height
exoContentFrameLayout.requestLayout()
}

private fun applyVideoZoom(videoZoom: VideoZoom) {
viewModel.setVideoZoom(videoZoom)
when (videoZoom) {
VideoZoom.BEST_FIT -> {
resetExoContentFrameWidthAndHeight()
binding.playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
videoZoomButton.setImageDrawable(this, coreUiR.drawable.ic_fit_screen)
}

VideoZoom.STRETCH -> {
resetExoContentFrameWidthAndHeight()
binding.playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
videoZoomButton.setImageDrawable(this, coreUiR.drawable.ic_aspect_ratio)
}

VideoZoom.CROP -> {
resetExoContentFrameWidthAndHeight()
binding.playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
videoZoomButton.setImageDrawable(this, coreUiR.drawable.ic_crop_landscape)
}

VideoZoom.HUNDRED_PERCENT -> {
currentVideoSize?.let {
exoContentFrameLayout.layoutParams.width = it.width
exoContentFrameLayout.layoutParams.height = it.height
exoContentFrameLayout.requestLayout()
}
binding.playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
videoZoomButton.setImageDrawable(this, coreUiR.drawable.ic_width_wide)
}
}
}
}

private val VideoSize.isPortrait: Boolean
Expand Down Expand Up @@ -707,3 +758,9 @@ fun Activity.prettyPrintIntent() {
}
}
}

inline fun <reified T : Enum<T>> T.next(): T {
val values = enumValues<T>()
val nextOrdinal = (ordinal + 1) % values.size
return values[nextOrdinal]
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import dev.anilbeesetti.nextplayer.core.domain.GetSortedPlaylistUseCase
import dev.anilbeesetti.nextplayer.core.model.ApplicationPreferences
import dev.anilbeesetti.nextplayer.core.model.PlayerPreferences
import dev.anilbeesetti.nextplayer.core.model.Resume
import dev.anilbeesetti.nextplayer.core.model.VideoZoom
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
Expand Down Expand Up @@ -102,6 +103,12 @@ class PlayerViewModel @Inject constructor(
}
}

fun setVideoZoom(videoZoom: VideoZoom) {
viewModelScope.launch {
preferencesRepository.updatePlayerPreferences { it.copy(playerVideoZoom = videoZoom) }
}
}

fun resetAllToDefaults() {
currentPlaybackPosition = null
currentPlaybackSpeed = 1f
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.anilbeesetti.nextplayer.feature.player.dialogs

import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.media3.common.util.UnstableApi
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.anilbeesetti.nextplayer.core.model.VideoZoom
import dev.anilbeesetti.nextplayer.core.ui.R

@UnstableApi
class VideoZoomOptionsDialogFragment(
private val currentVideoZoom: VideoZoom,
private val onVideoZoomOptionSelected: (videoZoom: VideoZoom) -> Unit
) : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val videoZoomValues = VideoZoom.values()

return activity?.let { activity ->
MaterialAlertDialogBuilder(activity)
.setTitle(getString(R.string.video_zoom))
.setSingleChoiceItems(
videoZoomValues.map { getString(it.nameRes()) }.toTypedArray(),
videoZoomValues.indexOfFirst { it == currentVideoZoom }
) { dialog, trackIndex ->
onVideoZoomOptionSelected(videoZoomValues[trackIndex])
dialog.dismiss()
}.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}

fun VideoZoom.nameRes(): Int {
val stringRes = when (this) {
VideoZoom.BEST_FIT -> R.string.best_fit
VideoZoom.STRETCH -> R.string.stretch
VideoZoom.CROP -> R.string.crop
VideoZoom.HUNDRED_PERCENT -> R.string.hundred_percent
}

return stringRes
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@
app:layout_constraintTop_toTopOf="parent"
app:tint="@android:color/white" />


<ImageButton
android:id="@+id/btn_video_zoom"
android:layout_width="wrap_content"
Expand All @@ -187,7 +186,7 @@
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/video_zoom"
android:padding="12dp"
android:src="@drawable/ic_aspect_ratio"
android:src="@drawable/ic_fit_screen"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_lock_controls"
app:layout_constraintTop_toTopOf="parent"
Expand Down