Skip to content

Commit

Permalink
Merge pull request #133 from yashx/native-gesture-control
Browse files Browse the repository at this point in the history
Added Native Player Gesture for Volume & Brightness
  • Loading branch information
Maxr1998 committed Sep 27, 2020
2 parents 45599fe + ad8acc8 commit 2f52b9d
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 2 deletions.
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

// 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>

0 comments on commit 2f52b9d

Please sign in to comment.