Skip to content
Open
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
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,6 @@ dependencies {

// Fuzzy search
implementation 'org.apache.commons:commons-text:1.15.0'

testImplementation "junit:junit:4.13.2"
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import android.content.Intent
import android.media.audiofx.AudioEffect
import android.os.Build
import android.os.Bundle
import android.view.HapticFeedbackConstants
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.Toolbar
Expand All @@ -44,6 +47,7 @@ import org.oxycblt.auxio.music.resolve
import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.playback.queue.QueueViewModel
import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.state.ScanDirection
import org.oxycblt.auxio.playback.ui.StyledSeekBar
import org.oxycblt.auxio.playback.ui.stepper.Direction
import org.oxycblt.auxio.playback.ui.stepper.StepperOverlay
Expand Down Expand Up @@ -153,7 +157,8 @@ class PlaybackPanelFragment :
// Set up actions
// TODO: Add better playback button accessibility
binding.playbackRepeat.setOnClickListener { playbackModel.toggleRepeatMode() }
binding.playbackSkipPrev.setOnClickListener { playbackModel.prev() }
binding.playbackSkipPrev.setupScanAction(ScanDirection.BACKWARD, playbackModel::prev)

binding.playbackPlayPause.apply {
@SuppressLint("RestrictedApi")
setCornerSpringForce(
Expand All @@ -164,7 +169,7 @@ class PlaybackPanelFragment :
)
setOnClickListener { playbackModel.togglePlaying() }
}
binding.playbackSkipNext.setOnClickListener { playbackModel.next() }
binding.playbackSkipNext.setupScanAction(ScanDirection.FORWARD, playbackModel::next)
binding.playbackShuffle.setOnClickListener { playbackModel.toggleShuffled() }
binding.playbackMore?.setOnClickListener {
playbackModel.song.value?.let {
Expand Down Expand Up @@ -218,6 +223,7 @@ class PlaybackPanelFragment :
// }

override fun onDestroyBinding(binding: FragmentPlaybackPanelBinding) {
playbackModel.stopScan()
equalizerLauncher = null
binding.playbackRepeat.clearPendingIcon()
binding.playbackSong.isSelected = false
Expand All @@ -228,6 +234,75 @@ class PlaybackPanelFragment :
binding.playbackPager?.adapter = null
}

private fun View.setupScanAction(direction: ScanDirection, click: () -> Unit) {
var scanning = false
var cancelled = false
val touchSlop = ViewConfiguration.get(context).scaledTouchSlop.toFloat()
val beginScan = Runnable {
scanning = true
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
playbackModel.startScan(direction)
}

setOnClickListener { click() }
setOnTouchListener { view, event ->
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
scanning = false
cancelled = false
view.isPressed = true
view.postDelayed(beginScan, ViewConfiguration.getLongPressTimeout().toLong())
}
MotionEvent.ACTION_MOVE -> {
if (
event.x !in -touchSlop..view.width + touchSlop ||
event.y !in -touchSlop..view.height + touchSlop
) {
view.removeCallbacks(beginScan)
if (scanning) {
playbackModel.stopScan()
scanning = false
}
cancelled = true
view.isPressed = false
}
}
MotionEvent.ACTION_UP -> {
view.removeCallbacks(beginScan)
view.isPressed = false
if (scanning) {
playbackModel.stopScan()
scanning = false
} else if (!cancelled) {
view.performClick()
}
}
MotionEvent.ACTION_CANCEL -> {
view.removeCallbacks(beginScan)
view.isPressed = false
if (scanning) {
playbackModel.stopScan()
scanning = false
}
}
}
true
}
addOnAttachStateChangeListener(
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(view: View) = Unit

override fun onViewDetachedFromWindow(view: View) {
view.removeCallbacks(beginScan)
if (scanning) {
playbackModel.stopScan()
scanning = false
}
}
}
)
}

override fun onMenuItemClick(item: MenuItem): Boolean {
if (item.itemId == R.id.action_open_equalizer) {
// Launch the system equalizer app, if possible.
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.playback.state.Progression
import org.oxycblt.auxio.playback.state.QueueChange
import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.state.ScanDirection
import org.oxycblt.auxio.playback.state.ShuffleMode
import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent
Expand Down Expand Up @@ -475,6 +476,18 @@ constructor(
}
}

/** Begin scanning through the current track while a transport button is held. */
fun startScan(direction: ScanDirection) {
L.d("Starting scan in direction $direction")
playbackManager.startScan(direction)
}

/** Stop scanning and return to normal playback. */
fun stopScan() {
L.d("Stopping scan")
playbackManager.stopScan()
}

// --- QUEUE FUNCTIONS ---

/** Skip to the next [Song]. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ constructor(
private val playbackManager: PlaybackStateManager,
private val playbackSettings: PlaybackSettings,
) : BaseAudioProcessor(), PlaybackStateManager.Listener, PlaybackSettings.Listener {
@Volatile
private var volume = 1f
set(value) {
field = value
// Processed bytes are no longer valid, flush the stream
flush()
}

/** The current linear ReplayGain amplification. */
val amplification: Float
get() = volume

fun attach() {
playbackManager.addListener(this)
playbackSettings.registerListener(this)
Expand Down
Loading