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

Added Native Player Gesture for Volume & Brightness #133

Merged
merged 1 commit into from
Sep 27, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/src/main/java/org/jellyfin/mobile/AppPreferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class AppPreferences(context: Context) {
val videoPlayerType: String
get() = sharedPreferences.getString(Constants.PREF_VIDEO_PLAYER_TYPE, VideoPlayerType.WEB_PLAYER)!!

val exoPlayerAllowSwipeGestures: Boolean
get() = sharedPreferences.getBoolean(Constants.PREF_EXOPLAYER_ALLOW_SWIPE_GESTURES, true)

val exoPlayerAllowBackgroundAudio: Boolean
get() = sharedPreferences.getBoolean(Constants.PREF_EXOPLAYER_ALLOW_BACKGROUND_AUDIO, false)
}
99 changes: 97 additions & 2 deletions app/src/main/java/org/jellyfin/mobile/player/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ package org.jellyfin.mobile.player

import android.annotation.SuppressLint
import android.app.PictureInPictureParams
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.media.AudioManager
import android.os.Build
import android.os.Bundle
import android.provider.Settings.System
import android.view.*
import android.widget.ImageButton
import android.widget.TextView
import android.widget.*
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
Expand All @@ -18,14 +20,19 @@ import androidx.core.view.postDelayed
import androidx.core.view.updatePadding
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.ui.PlayerView
import org.jellyfin.mobile.AppPreferences
import org.jellyfin.mobile.R
import org.jellyfin.mobile.utils.*
import org.jellyfin.mobile.utils.Constants.DEFAULT_CENTER_OVERLAY_TIMEOUT_MS
import org.jellyfin.mobile.utils.Constants.DEFAULT_CONTROLS_TIMEOUT_MS
import org.jellyfin.mobile.utils.Constants.DEFAULT_SEEK_TIME_MS
import org.koin.android.ext.android.inject
import kotlin.math.abs


class PlayerActivity : AppCompatActivity() {

private val appPreferences: AppPreferences by inject()
private val viewModel: PlayerViewModel by viewModels()
private val playerView: PlayerView by lazyView(R.id.player_view)
private val playerControlsView: View by lazyView(R.id.player_controls)
Expand All @@ -34,7 +41,21 @@ class PlayerActivity : AppCompatActivity() {
private val titleTextView: TextView by lazyView(R.id.track_title)
private val fullscreenSwitcher: ImageButton by lazyView(R.id.fullscreen_switcher)
private val unlockScreenButton: ImageButton by lazyView(R.id.unlock_screen_button)
private val gestureIndicatorOverlayLayout: LinearLayout by lazyView(R.id.gesture_overlay_layout)
private val gestureIndicatorOverlayImage: ImageView by lazyView(R.id.gesture_overlay_image)
private val gestureIndicatorOverlayProgress: ProgressBar by lazyView(R.id.gesture_overlay_progress)
private lateinit var playbackMenus: PlaybackMenus
private val audioManager: AudioManager by lazy { (getSystemService(Context.AUDIO_SERVICE) as AudioManager) }

private val swipeGesturesEnabled
get() = appPreferences.exoPlayerAllowSwipeGestures

/**
* Tracks a value during a swipe gesture (between multiple onScroll calls).
* When the gesture starts it's reset to an initial value and gets increased or decreased
* (depending on the direction) as the gesture progresses.
*/
private var swipeGestureValueTracker = -1f

/**
* Listener that watches the current device orientation.
Expand All @@ -58,6 +79,13 @@ class PlayerActivity : AppCompatActivity() {
playerView.hideController()
}

/**
* Runnable that hides [gestureIndicatorOverlayLayout]
*/
private val hideGestureIndicatorOverlayAction = Runnable {
gestureIndicatorOverlayLayout.isVisible = false
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
Expand Down Expand Up @@ -232,9 +260,76 @@ class PlayerActivity : AppCompatActivity() {
}
return true
}

override fun onScroll(firstEvent: MotionEvent, currentEvent: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
if (!swipeGesturesEnabled)
return false

yashx marked this conversation as resolved.
Show resolved Hide resolved
// Check whether swipe was oriented vertically
if (abs(distanceY / distanceX) < 2)
return false

val viewCenterX = playerView.measuredWidth / 2

// Distance to swipe to go from min to max
val distanceFull = playerView.measuredHeight * 0.66f
val ratioChange = distanceY / distanceFull

if (firstEvent.x.toInt() > viewCenterX) {
// Swiping on the right, change volume
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
if (swipeGestureValueTracker == -1f) swipeGestureValueTracker = currentVolume.toFloat()

val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val change = ratioChange * maxVolume
swipeGestureValueTracker += change

val toSet = swipeGestureValueTracker.toInt().coerceIn(0, maxVolume)
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, toSet, 0)

gestureIndicatorOverlayImage.setImageResource(R.drawable.ic_volume_white_24dp)
gestureIndicatorOverlayProgress.max = maxVolume
gestureIndicatorOverlayProgress.progress = toSet
} else {
// Swiping on the left, change brightness
val windowLayoutParams = window.attributes
if (swipeGestureValueTracker == -1f) {
swipeGestureValueTracker = windowLayoutParams.screenBrightness
if (swipeGestureValueTracker < 0f)
swipeGestureValueTracker = System.getFloat(contentResolver, System.SCREEN_BRIGHTNESS) / 255
}

swipeGestureValueTracker += ratioChange

val toSet = swipeGestureValueTracker.coerceIn(0f, 1f)
window.attributes = windowLayoutParams.apply {
screenBrightness = toSet
}

gestureIndicatorOverlayImage.setImageResource(R.drawable.ic_brightness_white_24dp)
gestureIndicatorOverlayProgress.max = 100
gestureIndicatorOverlayProgress.progress = (toSet * 100).toInt()
}

// Show gesture indicator
gestureIndicatorOverlayLayout.isVisible = true

return true
}
})

playerView.setOnTouchListener { _, event ->
if (playerView.useController) gestureDetector.onTouchEvent(event) else unlockDetector.onTouchEvent(event)
if (event.action == MotionEvent.ACTION_UP) {
// Hide gesture indicator after timeout, if shown
gestureIndicatorOverlayLayout.apply {
if (isVisible) {
removeCallbacks(hideGestureIndicatorOverlayAction)
postDelayed(hideGestureIndicatorOverlayAction, DEFAULT_CENTER_OVERLAY_TIMEOUT_MS.toLong())
}
}
swipeGestureValueTracker = -1f
}
true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SettingsActivity : AppCompatActivity() {
private val appPreferences: AppPreferences by inject()
private val settingsAdapter: PreferencesAdapter by lazy { PreferencesAdapter(buildSettingsScreen()) }
private lateinit var backgroundAudioPreference: Preference
private lateinit var swipeGesturesPreference: Preference

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -54,9 +55,15 @@ class SettingsActivity : AppCompatActivity() {
titleRes = R.string.pref_video_player_type_title
initialSelection = VideoPlayerType.WEB_PLAYER
defaultOnSelectionChange { selection ->
swipeGesturesPreference.enabled = selection == VideoPlayerType.EXO_PLAYER
backgroundAudioPreference.enabled = selection == VideoPlayerType.EXO_PLAYER
}
}
swipeGesturesPreference = checkBox(Constants.PREF_EXOPLAYER_ALLOW_SWIPE_GESTURES) {
titleRes = R.string.pref_exoplayer_allow_brightness_volume_gesture
enabled = appPreferences.videoPlayerType == VideoPlayerType.EXO_PLAYER
defaultValue = true
}
backgroundAudioPreference = checkBox(Constants.PREF_EXOPLAYER_ALLOW_BACKGROUND_AUDIO) {
titleRes = R.string.pref_exoplayer_allow_background_audio
enabled = appPreferences.videoPlayerType == VideoPlayerType.EXO_PLAYER
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/org/jellyfin/mobile/utils/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ object Constants {
const val PREF_DOWNLOAD_METHOD = "pref_download_method"
const val PREF_MUSIC_NOTIFICATION_ALWAYS_DISMISSIBLE = "pref_music_notification_always_dismissible"
const val PREF_VIDEO_PLAYER_TYPE = "pref_video_player_type"
const val PREF_EXOPLAYER_ALLOW_SWIPE_GESTURES = "pref_exoplayer_allow_swipe_gestures"
const val PREF_EXOPLAYER_ALLOW_BACKGROUND_AUDIO = "pref_exoplayer_allow_background_audio"

// InputManager commands
Expand Down Expand Up @@ -76,6 +77,7 @@ object Constants {
const val TICKS_PER_MILLISECOND = 10000
const val PLAYER_TIME_UPDATE_RATE = 3000L
const val DEFAULT_CONTROLS_TIMEOUT_MS = 2500
const val DEFAULT_CENTER_OVERLAY_TIMEOUT_MS = 250
const val DEFAULT_SEEK_TIME_MS = 5000L
const val SUPPORTED_VIDEO_PLAYER_PLAYBACK_ACTIONS: Long = PlaybackState.ACTION_PLAY_PAUSE or
PlaybackState.ACTION_PLAY or
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_brightness_white_24dp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z" />
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_volume_white_24dp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ffffff"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/layout/activity_player.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<include
layout="@layout/overlay_brightness_volume"
android:visibility="gone"
tools:visibility="visible" />

<ProgressBar
android:id="@+id/loading_indicator"
android:layout_width="64dp"
Expand Down
28 changes: 28 additions & 0 deletions app/src/main/res/layout/overlay_brightness_volume.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/gesture_overlay_layout"
android:layout_width="@dimen/exo_gesture_overlay_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/playback_info_background"
android:clickable="false"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="16dp">

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/gesture_overlay_image"
android:layout_width="@dimen/exo_gesture_overlay_image_size"
android:layout_height="@dimen/exo_gesture_overlay_image_size"
android:layout_marginVertical="16dp"
tools:srcCompat="@drawable/ic_brightness_white_24dp" />

<ProgressBar
android:id="@+id/gesture_overlay_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
tools:progress="50" />
</LinearLayout>
2 changes: 2 additions & 0 deletions app/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
<dimen name="exo_bottom_controls_size">42dp</dimen>
<dimen name="exo_bottom_controls_padding">8dp</dimen>
<dimen name="exo_bottom_controls_margin">8dp</dimen>
<dimen name="exo_gesture_overlay_width">160dp</dimen>
<dimen name="exo_gesture_overlay_image_size">72dp</dimen>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@
<string name="video_player_native_description">Based on ExoPlayer, supports more video formats and codecs, and is more integrated into the OS</string>
<string name="video_player_external_description">External video playback apps like MX Player and VLC</string>
<string name="pref_exoplayer_allow_background_audio">Allow playing audio in the background in native player</string>
<string name="pref_exoplayer_allow_brightness_volume_gesture">Allow brightness and volume gestures in native player</string>
</resources>