From 3bafc17d0c1ed654b57282c3d818691c25991a1d Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Mon, 12 Oct 2020 16:02:26 -0600 Subject: [PATCH] Add durations to PlaybackFragment Add the song duration & the current duration to PlaybackFragment, update the SeekBar in PlaybackFragment to reflect those durations. --- app/build.gradle | 6 +-- .../java/org/oxycblt/auxio/music/Models.kt | 1 + .../auxio/playback/PlaybackFragment.kt | 41 ++++++++++++++++++- .../auxio/playback/PlaybackViewModel.kt | 36 +++++++++++----- .../main/res/drawable/ui_header_dividers.xml | 2 +- app/src/main/res/layout/fragment_playback.xml | 35 +++++++++++++--- app/src/main/res/values/dimens.xml | 1 + build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 9 files changed, 103 insertions(+), 27 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b6a04720d..aca61c22c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,11 +48,11 @@ dependencies { // General implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.activity:activity:1.2.0-beta01' - implementation 'androidx.fragment:fragment:1.3.0-beta01' + implementation 'androidx.activity:activity-ktx:1.2.0-beta01' + implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01' // Layout - implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.2' // Lifecycle implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt index 4b2c2afd7..be2d8102a 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -14,6 +14,7 @@ sealed class BaseModel { } // Song +// TODO: Maybe move durations to a solely-millis system data class Song( override val id: Long = -1, override var name: String, diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index 952d6dc09..76d87ec4f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -3,9 +3,11 @@ package org.oxycblt.auxio.playback import android.content.res.ColorStateList import android.graphics.drawable.AnimatedVectorDrawable import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.SeekBar import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -15,11 +17,11 @@ import org.oxycblt.auxio.databinding.FragmentPlaybackBinding import org.oxycblt.auxio.theme.accent import org.oxycblt.auxio.theme.toColor -class PlaybackFragment : Fragment() { +class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { private val playbackModel: PlaybackViewModel by activityViewModels() + // TODO: Implement media controls // TODO: Implement nav to artists/albums - // TODO: Add a full playback fragment // TODO: Possibly implement a trackbar with a spectrum shown as well. override fun onCreateView( inflater: LayoutInflater, @@ -31,6 +33,7 @@ class PlaybackFragment : Fragment() { // Create accents & icons to use val accentColor = ColorStateList.valueOf(accent.first.toColor(requireContext())) val inactiveColor = ColorStateList.valueOf(R.color.control_color.toColor(requireContext())) + val normalTextColor = binding.playbackDurationCurrent.currentTextColor val iconPauseToPlay = ContextCompat.getDrawable( requireContext(), R.drawable.ic_pause_to_play @@ -50,6 +53,7 @@ class PlaybackFragment : Fragment() { // Make marquee scroll work binding.playbackSong.isSelected = true + binding.playbackSeekBar.setOnSeekBarChangeListener(this) // --- VIEWMODEL SETUP -- @@ -73,6 +77,39 @@ class PlaybackFragment : Fragment() { } } + playbackModel.isSeeking.observe(viewLifecycleOwner) { + // Highlight the current duration if the user is seeking, and revert it if not. + if (it) { + binding.playbackDurationCurrent.setTextColor(accentColor) + } else { + binding.playbackDurationCurrent.setTextColor(normalTextColor) + } + } + + // Updates for the current duration TextView/Seekbar + playbackModel.formattedCurrentDuration.observe(viewLifecycleOwner) { + binding.playbackDurationCurrent.text = it + } + + playbackModel.formattedSeekBarProgress.observe(viewLifecycleOwner) { + binding.playbackSeekBar.progress = it + } + + Log.d(this::class.simpleName, "Fragment Created.") + return binding.root } + + // Seeking callbacks + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + playbackModel.updateCurrentDurationWithProgress(progress) + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + playbackModel.setSeekingStatus(true) + } + + override fun onStopTrackingTouch(seekBar: SeekBar) { + playbackModel.setSeekingStatus(false) + } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index ddaed27b6..d406618ec 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -2,8 +2,10 @@ package org.oxycblt.auxio.playback import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.music.toDuration // TODO: Implement media controls // TODO: Add the playback service itself @@ -13,12 +15,23 @@ class PlaybackViewModel : ViewModel() { private val mCurrentSong = MutableLiveData() val currentSong: LiveData get() = mCurrentSong - private val mShouldOpenPlayback = MutableLiveData() - val shouldOpenPlayback: LiveData get() = mShouldOpenPlayback + private val mCurrentDuration = MutableLiveData(0L) + val currentDuration: LiveData get() = mCurrentDuration private val mIsPlaying = MutableLiveData(false) val isPlaying: LiveData get() = mIsPlaying + private val mIsSeeking = MutableLiveData(false) + val isSeeking: LiveData get() = mIsSeeking + + val formattedCurrentDuration = Transformations.map(currentDuration) { + it.toDuration() + } + + val formattedSeekBarProgress = Transformations.map(currentDuration) { + ((it.toDouble() / mCurrentSong.value!!.seconds) * 100).toInt() + } + fun updateSong(song: Song) { mCurrentSong.value = song @@ -27,16 +40,17 @@ class PlaybackViewModel : ViewModel() { } } - fun openPlayback() { - mShouldOpenPlayback.value = true - } - - fun doneWithOpenPlayback() { - mShouldOpenPlayback.value = false - } - - // Invert, not directly set the p + // Invert, not directly set the playing status fun invertPlayingStatus() { mIsPlaying.value = !mIsPlaying.value!! } + + fun setSeekingStatus(status: Boolean) { + mIsSeeking.value = status + } + + fun updateCurrentDurationWithProgress(progress: Int) { + mCurrentDuration.value = + ((progress.toDouble() / 100) * mCurrentSong.value!!.seconds).toLong() + } } diff --git a/app/src/main/res/drawable/ui_header_dividers.xml b/app/src/main/res/drawable/ui_header_dividers.xml index 6970a55fd..55b7c783f 100644 --- a/app/src/main/res/drawable/ui_header_dividers.xml +++ b/app/src/main/res/drawable/ui_header_dividers.xml @@ -16,6 +16,6 @@ https://stackoverflow.com/a/61157571/14143986 + android:radius="@dimen/size_divider_ripple" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_playback.xml b/app/src/main/res/layout/fragment_playback.xml index e743bd240..8f18348c2 100644 --- a/app/src/main/res/layout/fragment_playback.xml +++ b/app/src/main/res/layout/fragment_playback.xml @@ -41,9 +41,9 @@ android:layout_height="0dp" android:layout_margin="@dimen/margin_mid_large" android:contentDescription="@{@string/description_album_cover(song.name)}" - app:layout_constraintDimensionRatio="1:1" app:coverArt="@{song}" app:layout_constraintBottom_toTopOf="@+id/playback_song" + app:layout_constraintDimensionRatio="1:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/playback_toolbar" @@ -88,10 +88,10 @@ android:id="@+id/playback_album" android:layout_width="0dp" android:layout_height="wrap_content" - android:ellipsize="end" android:layout_marginStart="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large" - android:layout_marginBottom="@dimen/margin_medium" + android:layout_marginBottom="16dp" + android:ellipsize="end" android:singleLine="true" android:text="@{song.album.name}" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" @@ -105,23 +105,46 @@ android:id="@+id/playback_seek_bar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/margin_medium" android:paddingStart="@dimen/margin_mid_large" android:paddingEnd="@dimen/margin_mid_large" + android:thumbOffset="@dimen/offset_thumb" + android:clickable="true" + android:focusable="true" android:progressBackgroundTint="?android:attr/colorControlNormal" android:progressTint="?android:attr/colorPrimary" android:splitTrack="false" android:thumbTint="?android:attr/colorPrimary" - app:layout_constraintBottom_toTopOf="@+id/playback_play_pause" + app:layout_constraintBottom_toTopOf="@+id/playback_duration_current" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" tools:progress="70" /> + + + + 4dp + 4dp \ No newline at end of file diff --git a/build.gradle b/build.gradle index 33a022023..3c34f805b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.4.0" + ext.kotlin_version = "1.4.10" repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.2' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d6a1cb592..d31e46712 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Aug 17 09:36:07 MDT 2020 +#Mon Oct 12 13:43:13 MDT 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip