Browse Source

UI and animation improvements for the audio player

merge-requests/382/head
Nicolas Pomepuy 7 years ago
committed by Geoffrey Métais
parent
commit
e7e2447fa7
  1. 32
      vlc-android/res/drawable/advanced_options_background_dark.xml
  2. 3
      vlc-android/res/drawable/advanced_options_background_light.xml
  3. 29
      vlc-android/res/layout-land/audio_player.xml
  4. 26
      vlc-android/res/layout/audio_player.xml
  5. 1
      vlc-android/res/layout/player_options.xml
  6. 1
      vlc-android/res/values/colors.xml
  7. 2
      vlc-android/res/values/styles.xml
  8. 8
      vlc-android/src/org/videolan/vlc/gui/AudioPlayerContainerActivity.kt
  9. 85
      vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt

32
vlc-android/res/drawable/advanced_options_background_dark.xml

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ *************************************************************************
~ advanced_options_background_dark.xml
~ **************************************************************************
~ Copyright © 2019 VLC authors and VideoLAN
~ Author: Nicolas POMEPUY
~ This program is free software; you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation; either version 2 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program; if not, write to the Free Software
~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
~ ***************************************************************************
~
~
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/grey875"></solid>
<corners
android:topLeftRadius="4dp"
android:bottomLeftRadius="4dp"></corners>
</shape>

3
vlc-android/res/drawable/advanced_options_background_light.xml

@ -2,8 +2,7 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="@color/whitetransparent" >
<solid android:color="@color/white">
</solid>
<corners
android:topLeftRadius="4dp"

29
vlc-android/res/layout-land/audio_player.xml

@ -48,6 +48,17 @@
android:paddingRight="0px"
android:progressDrawable="?attr/progress_mini_player" />
<View
android:id="@+id/progressBarMask"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@id/progressBar"
app:layout_constraintEnd_toEndOf="@id/progressBar"
app:layout_constraintTop_toTopOf="@id/progressBar"
app:layout_constraintBottom_toBottomOf="@id/progressBar"
android:alpha="0"
android:background="?attr/background_default_darker" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/header"
android:layout_width="0dp"
@ -87,24 +98,26 @@
/>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/playlist_search_text"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:focusable="true"
android:visibility="gone"
android:layout_marginTop="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/header_play_pause"
app:layout_constraintStart_toEndOf="@+id/playlist_playasaudio_off"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp">
app:layout_constraintTop_toTopOf="parent">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/search_hint"
android:inputType="textFilter"
android:imeOptions="actionSearch"/>
android:imeOptions="actionSearch"
android:inputType="textFilter" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView

26
vlc-android/res/layout/audio_player.xml

@ -50,6 +50,17 @@
android:paddingRight="0px"
android:progressDrawable="?attr/progress_mini_player"/>
<View
android:id="@+id/progressBarMask"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="@id/progressBar"
app:layout_constraintEnd_toEndOf="@id/progressBar"
app:layout_constraintTop_toTopOf="@id/progressBar"
app:layout_constraintBottom_toBottomOf="@id/progressBar"
android:alpha="0"
android:background="?attr/background_default_darker" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/header"
android:layout_width="0dp"
@ -89,12 +100,14 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/playlist_search_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:focusable="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/header_play_pause"
app:layout_constraintStart_toEndOf="@+id/playlist_playasaudio_off"
app:layout_constraintTop_toTopOf="parent">
@ -104,7 +117,7 @@
android:layout_height="wrap_content"
android:hint="@string/search_hint"
android:imeOptions="actionSearch"
android:inputType="textFilter"/>
android:inputType="textFilter" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
@ -225,14 +238,13 @@
<View
android:id="@+id/bottom_bar"
android:layout_width="0dp"
android:layout_height="72dp"
android:id="@+id/bottom_bar"
android:background="?attr/audio_player_transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:background="?attr/background_default_darker"
app:layout_constraintBottom_toBottomOf="@+id/backgroundView"
/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView

1
vlc-android/res/layout/player_options.xml

@ -11,6 +11,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|end"
android:elevation="8dp"
android:background="?attr/advanced_options_bg"
android:clipToPadding="false"
android:paddingTop="@dimen/default_margin"

1
vlc-android/res/values/colors.xml

@ -52,6 +52,7 @@
<color name="blacktransparent">#b4000000</color>
<color name="whitetransparent">#b4ffffff</color>
<color name="white_less_transparent">#ccffffff</color>
<color name="ripple_white">#44ffffff</color>
<color name="white_more_transparent">#33ffffff</color>
<color name="white_audio_player_transparent">#aaffffff</color>

2
vlc-android/res/values/styles.xml

@ -192,7 +192,7 @@
<item name="gridview_progressbar">@drawable/gridview_progressbar_w</item>
<item name="progress_mini_player">@drawable/progress_mini_player_dark</item>
<item name="advanced_options_style">@style/Theme.MaterialComponents.Dialog</item>
<item name="advanced_options_bg">@drawable/advanced_options_background</item>
<item name="advanced_options_bg">@drawable/advanced_options_background_dark</item>
<item name="ic_playasaudio_off">@drawable/ic_playasaudio_off_w</item>
<item name="ic_playasaudio_on">@drawable/ic_playasaudio_on_w</item>
<item name="ic_popup_expand">@drawable/ic_popup_expand_w</item>

8
vlc-android/src/org/videolan/vlc/gui/AudioPlayerContainerActivity.kt

@ -132,6 +132,14 @@ open class AudioPlayerContainerActivity : BaseActivity() {
bottomSheetBehavior = from(audioPlayerContainer!!) as BottomSheetBehavior<*>
bottomSheetBehavior!!.peekHeight = resources.getDimensionPixelSize(R.dimen.player_peek_height)
bottomSheetBehavior!!.setBottomSheetCallback(audioPlayerBottomSheetCallback)
bottomSheetBehavior!!.addBottomSheetCallback(object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
audioPlayer?.onSlide(slideOffset)
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
}
})
showTipViewIfNeeded(R.id.audio_player_tips, PREF_AUDIOPLAYER_TIPS_SHOWN)
}

85
vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt

@ -25,7 +25,6 @@ import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Bundle
@ -43,13 +42,10 @@ import android.widget.SeekBar.OnSeekBarChangeListener
import androidx.annotation.MainThread
import androidx.annotation.RequiresPermission
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.Snackbar
@ -59,6 +55,7 @@ import kotlinx.coroutines.channels.actor
import org.videolan.medialibrary.Tools
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
import org.videolan.tools.isStarted
import org.videolan.tools.px
import org.videolan.vlc.PlaybackService
import org.videolan.vlc.R
import org.videolan.vlc.VLCApplication
@ -74,6 +71,8 @@ import org.videolan.vlc.media.PlaylistManager.Companion.hasMedia
import org.videolan.vlc.util.*
import org.videolan.vlc.viewmodels.PlaybackProgress
import org.videolan.vlc.viewmodels.PlaylistModel
import kotlin.math.max
import kotlin.math.min
private const val TAG = "VLC/AudioPlayer"
private const val SEARCH_TIMEOUT_MILLIS = 10000L
@ -93,13 +92,6 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
private var showRemainingTime = false
private var previewingSeek = false
private var advFuncVisible = false
private var playlistSwitchVisible = false
private var searchVisible = false
private var searchTextVisible = false
private var headerPlayPauseVisible = false
private var progressBarVisible = false
private var headerTimeVisible = false
private var playerState = 0
private var currentCoverArt: String? = null
private lateinit var pauseToPlay: AnimatedVectorDrawableCompat
@ -145,7 +137,6 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
val touchHelper = ItemTouchHelper(callback)
touchHelper.attachToRecyclerView(binding.songsList)
setHeaderVisibilities(false, false, true, true, true, false)
binding.fragment = this
binding.next.setOnTouchListener(LongSeekListener(true,
@ -166,6 +157,7 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
pauseToPlay = AnimatedVectorDrawableCompat.create(requireActivity(), R.drawable.anim_pause_play)!!
playToPauseSmall = AnimatedVectorDrawableCompat.create(requireActivity(), R.drawable.anim_play_pause)!!
pauseToPlaySmall = AnimatedVectorDrawableCompat.create(requireActivity(), R.drawable.anim_pause_play)!!
onSlide(0f)
}
override fun onResume() {
@ -310,7 +302,6 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
binding.backgroundView.setImageBitmap(blurredCover)
binding.backgroundView.visibility = View.VISIBLE
binding.songsList.setBackgroundResource(0)
binding.header.setBackgroundColor(UiTools.getColorFromAttribute(requireContext(), R.attr.audio_player_transparent))
} else setDefaultBackground()
}
}
@ -320,7 +311,6 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
@MainThread
private fun setDefaultBackground() {
binding.songsList.setBackgroundResource(DEFAULT_BACKGROUND_ID)
binding.header.setBackgroundResource(DEFAULT_BACKGROUND_ID)
binding.backgroundView.visibility = View.INVISIBLE
}
@ -398,35 +388,12 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
optionsDelegate.show(PlayerOptionType.ADVANCED)
}
private fun setHeaderVisibilities(advFuncVisible: Boolean, playlistSwitchVisible: Boolean,
headerPlayPauseVisible: Boolean, progressBarVisible: Boolean,
headerTimeVisible: Boolean, searchVisible: Boolean,
filter: Boolean = false) {
this.advFuncVisible = !filter && advFuncVisible
this.playlistSwitchVisible = !filter && playlistSwitchVisible && resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE
this.headerPlayPauseVisible = !filter && headerPlayPauseVisible
this.progressBarVisible = !filter && progressBarVisible
this.headerTimeVisible = !filter && headerTimeVisible
this.searchVisible = !filter && searchVisible
this.searchTextVisible = filter
restoreHeaderButtonVisibilities()
}
private fun restoreHeaderButtonVisibilities() {
binding.progressBar.visibility = if (progressBarVisible) View.VISIBLE else View.INVISIBLE
val cl = binding.header
TransitionManager.beginDelayedTransition(cl, AutoTransition().setDuration(200))
ConstraintSet().apply {
clone(cl)
setVisibility(R.id.playlist_search, if (searchVisible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
setVisibility(R.id.playlist_switch, if (playlistSwitchVisible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
setVisibility(R.id.adv_function, if (advFuncVisible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
setVisibility(R.id.header_play_pause, if (headerPlayPauseVisible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
setVisibility(R.id.header_time, if (headerTimeVisible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
setVisibility(R.id.playlist_search_text, if (searchTextVisible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
setVisibility(R.id.audio_media_switcher, if (searchTextVisible) ConstraintSet.GONE else ConstraintSet.VISIBLE)
applyTo(cl)
}
private fun manageSearchVisibilities(filter: Boolean = false) {
binding.playlistSearch.alpha = if (filter) 0f else 1f
binding.playlistSwitch.alpha = if (filter) 0f else 1f
binding.advFunction.alpha = if (filter) 0f else 1f
binding.audioMediaSwitcher.alpha = if (filter) 0f else 1f
binding.playlistSearchText.visibility = if (filter) View.VISIBLE else View.GONE
}
fun onABRepeat(v: View) {
@ -434,7 +401,7 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
}
fun onSearchClick(v: View) {
setHeaderVisibilities(false, false, false, false, false, false, true)
manageSearchVisibilities(true)
binding.playlistSearchText.editText?.requestFocus()
if (binding.showCover) onPlaylistSwitchClick(binding.playlistSwitch)
val imm = v.context.applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
@ -465,8 +432,7 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
addTextChangedListener(this@AudioPlayer)
}
UiTools.setKeyboardVisibility(binding.playlistSearchText, false)
if (playerState == BottomSheetBehavior.STATE_COLLAPSED) setHeaderVisibilities(false, false, true, true, true, false)
else setHeaderVisibilities(true, true, false, false, false, true)
manageSearchVisibilities(false)
return true
}
@ -571,19 +537,36 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, CoroutineS
when (newState) {
BottomSheetBehavior.STATE_COLLAPSED -> {
backPressed()
binding.header.setBackgroundResource(DEFAULT_BACKGROUND_DARKER_ID)
setHeaderVisibilities(advFuncVisible = false, playlistSwitchVisible = false, headerPlayPauseVisible = true, progressBarVisible = true, headerTimeVisible = true, searchVisible = false)
onSlide(0f)
}
BottomSheetBehavior.STATE_EXPANDED -> {
binding.header.setBackgroundColor(UiTools.getColorFromAttribute(requireContext(), R.attr.audio_player_transparent))
setHeaderVisibilities(advFuncVisible = true, playlistSwitchVisible = true, headerPlayPauseVisible = false, progressBarVisible = false, headerTimeVisible = false, searchVisible = true)
onSlide(1f)
showPlaylistTips()
playlistAdapter.currentIndex = playlistModel.currentMediaPosition
}
// else -> binding.header.setBackgroundResource(0)
}
}
fun onSlide(slideOffset: Float) {
binding.progressBarMask.alpha = slideOffset
if (slideOffset != 1f) {
clearSearch()
}
binding.playlistSearch.alpha = slideOffset
binding.playlistSwitch.alpha = slideOffset
binding.advFunction.alpha = slideOffset
binding.headerPlayPause.alpha = 1 - slideOffset
binding.headerTime.alpha = 1 - slideOffset
val translationOffset = min(1f, max(0f, (slideOffset * 1.4f) - 0.2f))
binding.playlistSearch.translationY = (1 - translationOffset) * 60.px
binding.playlistSwitch.translationY = (1 - translationOffset) * 60.px
binding.advFunction.translationY = (1 - translationOffset) * 60.px
binding.headerPlayPause.translationY = -translationOffset * 60.px
binding.headerTime.translationY = -translationOffset * 60.px
}
private var timelineListener: OnSeekBarChangeListener = object : OnSeekBarChangeListener {
override fun onStopTrackingTouch(seekBar: SeekBar) {}

Loading…
Cancel
Save