From 023bd8071ad2594f8a19cd63c863cb96d5e842e3 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Thu, 22 Jan 2026 22:09:49 -0800 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20use=20existing=20future=20when=20add?= =?UTF-8?q?ing=20tracks,=20dialed=20random=20album=20trac=E2=80=A6=20(#373?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: use existing future when adding tracks, dialed random album tracks back to 100, vs 1000. --- .../tempo/service/BaseMediaService.kt | 10 ++++++++-- .../tempo/service/MediaManager.java | 17 ++++++----------- .../tempo/viewmodel/HomeViewModel.java | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/service/BaseMediaService.kt b/app/src/main/java/com/cappielloantonio/tempo/service/BaseMediaService.kt index 6f5fc52d..c8cb846f 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/service/BaseMediaService.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/service/BaseMediaService.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.PendingIntent.FLAG_IMMUTABLE import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.TaskStackBuilder +import android.content.ComponentName import android.content.Context import android.content.Intent import android.net.ConnectivityManager @@ -138,8 +139,13 @@ open class BaseMediaService : MediaLibraryService() { if (item.mediaMetadata.extras != null) MediaManager.scrobble(item, false) - if (player.nextMediaItemIndex == C.INDEX_UNSET) - MediaManager.continuousPlay(player.currentMediaItem) + if (player.nextMediaItemIndex == C.INDEX_UNSET) { + val browserFuture = MediaBrowser.Builder( + this@BaseMediaService, + SessionToken(this@BaseMediaService, ComponentName(this@BaseMediaService, this@BaseMediaService::class.java)) + ).buildAsync() + MediaManager.continuousPlay(player.currentMediaItem, browserFuture) + } } if (player is ExoPlayer) { diff --git a/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java b/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java index ba2cee60..44c5f974 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java +++ b/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java @@ -444,25 +444,20 @@ public class MediaManager { } @OptIn(markerClass = UnstableApi.class) - public static void continuousPlay(MediaItem mediaItem) { + public static void continuousPlay(MediaItem mediaItem, ListenableFuture existingBrowserFuture) { if (mediaItem != null && Preferences.isContinuousPlayEnabled() && Preferences.isInstantMixUsable()) { Preferences.setLastInstantMix(); - LiveData> instantMix = getSongRepository().getContinuousMix(mediaItem.mediaId,25); + LiveData> instantMix = getSongRepository().getContinuousMix(mediaItem.mediaId, 25); instantMix.observeForever(new Observer>() { @Override public void onChanged(List media) { - if (media != null) { - Log.e(TAG, "continuous play"); - ListenableFuture mediaBrowserListenableFuture = new MediaBrowser.Builder( - App.getContext(), - new SessionToken(App.getContext(), new ComponentName(App.getContext(), MediaService.class)) - ).buildAsync(); - - enqueue(mediaBrowserListenableFuture, media, true); + if (media != null && existingBrowserFuture != null) { + Log.d(TAG, "Continuous play: adding " + media.size() + " tracks"); + enqueue(existingBrowserFuture, media, false); } - + instantMix.removeObserver(this); } }); diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java index 42ff0b25..3cd90d0d 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/HomeViewModel.java @@ -101,7 +101,7 @@ public class HomeViewModel extends AndroidViewModel { } public LiveData> getRandomShuffleSample() { - return songRepository.getRandomSample(1000, null, null); + return songRepository.getRandomSample(100, null, null); } public LiveData> getChronologySample(LifecycleOwner owner) { From a23a663d320e1c170402d5644db6a056fe7d3ab1 Mon Sep 17 00:00:00 2001 From: skajmer <64442855+skajmer@users.noreply.github.com> Date: Sat, 24 Jan 2026 07:15:51 +0100 Subject: [PATCH 2/7] chore(i18n): Update Polish translation (#374) * Add #338 * Add #3700 (strings.xml) * Add #370 (arrays.xml) --- app/src/main/res/values-pl/arrays.xml | 13 +++++++++++-- app/src/main/res/values-pl/strings.xml | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-pl/arrays.xml b/app/src/main/res/values-pl/arrays.xml index f3105914..9642a999 100644 --- a/app/src/main/res/values-pl/arrays.xml +++ b/app/src/main/res/values-pl/arrays.xml @@ -239,7 +239,16 @@ 4 8 - + + + Po nazwie + Losowo + + + ORDER_BY_NAME + ORDER_BY_RANDOM + + Minimum 0 gwiazdek Minimum 1 gwiazdka @@ -254,4 +263,4 @@ 3 4 - \ No newline at end of file + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 15f23932..32d5b11f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -39,6 +39,7 @@ Pobrani wykonawcy Wykonawcy oznaczeni gwiazdką Wykonawcy + Brak dodatkowych informacji o wykonawcy Radio Odtwarzanie losowe Zmień układ @@ -350,6 +351,7 @@ Jeżeli włączone, widoczna będzie sekcja z folderami z muzyką. Weź pod uwagę że żeby funkcja nawigacji po folderach działała poprawnie, serwer musi wspierać tę funkcję. Pokazuj podcasty Jeżeli włączone, widoczna będzie sekcja z podcastami. Zrestartuj aplikację aby, zmiany przyniosły pełny efekt. + Sortowanie playlist Pokaż jakość audio Bitrate i format audio będzie pokazywany dla każdego utworu. Pokaż ocenę piosenek w gwiazdkach From e5b7756f960035d4233b65df4953fb21da85579c Mon Sep 17 00:00:00 2001 From: Pascal Grittmann Date: Sat, 24 Jan 2026 17:28:20 +0100 Subject: [PATCH 3/7] fix: Check for OpenSubsonic extensions also with password authentication (#375) check for OpenSubsonic extensions also with pwd auth Co-authored-by: eddyizm --- .../com/cappielloantonio/tempo/ui/activity/MainActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java b/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java index 156805a2..e0567913 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java @@ -384,7 +384,7 @@ public class MainActivity extends BaseActivity { } private void pingServer() { - if (Preferences.getToken() == null) return; + if (Preferences.getToken() == null && Preferences.getPassword() == null) return; if (Preferences.isInUseServerAddressLocal()) { mainViewModel.ping().observe(this, subsonicResponse -> { @@ -428,7 +428,7 @@ public class MainActivity extends BaseActivity { } private void getOpenSubsonicExtensions() { - if (Preferences.getToken() != null) { + if (Preferences.getToken() != null || Preferences.getPassword() != null) { mainViewModel.getOpenSubsonicExtensions().observe(this, openSubsonicExtensions -> { if (openSubsonicExtensions != null) { Preferences.setOpenSubsonicExtensions(openSubsonicExtensions); From bde34d3df097f66746c49741035cbeecad058389 Mon Sep 17 00:00:00 2001 From: drakeerv <60192521+drakeerv@users.noreply.github.com> Date: Sat, 24 Jan 2026 11:28:47 -0500 Subject: [PATCH 4/7] feat: Implement duration and seeking for transcodes (#358) Co-authored-by: eddyizm --- .../tempo/util/DynamicMediaSourceFactory.kt | 13 +- .../tempo/util/MusicUtil.java | 8 +- .../tempo/util/TranscodingMediaSource.kt | 322 ++++++++++++++++++ 3 files changed, 340 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/cappielloantonio/tempo/util/TranscodingMediaSource.kt diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/DynamicMediaSourceFactory.kt b/app/src/main/java/com/cappielloantonio/tempo/util/DynamicMediaSourceFactory.kt index 31dc172a..519afe91 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/DynamicMediaSourceFactory.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/DynamicMediaSourceFactory.kt @@ -46,8 +46,17 @@ class DynamicMediaSourceFactory( else -> { val extractorsFactory: ExtractorsFactory = DefaultExtractorsFactory() - ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory) - .createMediaSource(mediaItem) + val progressiveFactory = ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory) + + val uri = mediaItem.localConfiguration?.uri + val isTranscoding = uri?.getQueryParameter("maxBitRate") != null || + (uri?.getQueryParameter("format") != null && uri?.getQueryParameter("format") != "raw") + + if (isTranscoding && OpenSubsonicExtensionsUtil.isTranscodeOffsetExtensionAvailable()) { + TranscodingMediaSource(mediaItem, dataSourceFactory, progressiveFactory) + } else { + progressiveFactory.createMediaSource(mediaItem) + } } } } diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java b/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java index cd2c7a36..c1656899 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java +++ b/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java @@ -31,7 +31,7 @@ public class MusicUtil { private static final Pattern BITRATE_PATTERN = Pattern.compile("&maxBitRate=\\d+"); private static final Pattern FORMAT_PATTERN = Pattern.compile("&format=\\w+"); - public static Uri getStreamUri(String id) { + public static Uri getStreamUri(String id, int timeOffset) { Map params = App.getSubsonicClientInstance(false).getParams(); StringBuilder uri = new StringBuilder(); @@ -58,6 +58,8 @@ public class MusicUtil { uri.append("&format=").append(getTranscodingFormatPreference()); if (Preferences.askForEstimateContentLength()) uri.append("&estimateContentLength=true"); + if (timeOffset > 0) + uri.append("&timeOffset=").append(timeOffset); uri.append("&id=").append(id); @@ -66,6 +68,10 @@ public class MusicUtil { return Uri.parse(uri.toString()); } + public static Uri getStreamUri(String id) { + return getStreamUri(id, 0); + } + public static Uri updateStreamUri(Uri uri) { String s = uri.toString(); Matcher m1 = BITRATE_PATTERN.matcher(s); diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/TranscodingMediaSource.kt b/app/src/main/java/com/cappielloantonio/tempo/util/TranscodingMediaSource.kt new file mode 100644 index 00000000..14270ff9 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/util/TranscodingMediaSource.kt @@ -0,0 +1,322 @@ +package com.cappielloantonio.tempo.util + +import androidx.annotation.OptIn +import androidx.media3.common.C +import androidx.media3.common.MediaItem +import androidx.media3.common.Timeline +import androidx.media3.common.util.UnstableApi +import androidx.media3.common.util.Util +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.TransferListener +import androidx.media3.decoder.DecoderInputBuffer +import androidx.media3.exoplayer.FormatHolder +import androidx.media3.exoplayer.LoadingInfo +import androidx.media3.exoplayer.SeekParameters +import androidx.media3.exoplayer.source.CompositeMediaSource +import androidx.media3.exoplayer.source.ForwardingTimeline +import androidx.media3.exoplayer.source.MediaPeriod +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.source.ProgressiveMediaSource +import androidx.media3.exoplayer.source.SampleStream +import androidx.media3.exoplayer.trackselection.ExoTrackSelection +import androidx.media3.exoplayer.upstream.Allocator + +@OptIn(UnstableApi::class) +class TranscodingMediaSource( + private val mediaItem: MediaItem, + private val dataSourceFactory: DataSource.Factory, + private val progressiveMediaSourceFactory: ProgressiveMediaSource.Factory +) : CompositeMediaSource() { + + private var durationUs: Long = C.TIME_UNSET + private var currentSource: MediaSource? = null + + init { + val extras = mediaItem.mediaMetadata.extras + if (extras != null && extras.containsKey("duration")) { + val seconds = extras.getInt("duration") + if (seconds > 0) { + durationUs = Util.msToUs(seconds * 1000L) + } + } + } + + override fun getMediaItem() = mediaItem + + override fun prepareSourceInternal(mediaTransferListener: TransferListener?) { + super.prepareSourceInternal(mediaTransferListener) + val initialSource = progressiveMediaSourceFactory.createMediaSource(mediaItem) + currentSource = initialSource + prepareChildSource(null, initialSource) + } + + override fun onChildSourceInfoRefreshed( + childSourceId: Void?, + mediaSource: MediaSource, + newTimeline: Timeline + ) { + val timeline = + if (durationUs != C.TIME_UNSET) { + DurationOverridingTimeline(newTimeline, durationUs) + } else { + newTimeline + } + refreshSourceInfo(timeline) + } + + override fun createPeriod( + id: MediaSource.MediaPeriodId, + allocator: Allocator, + startPositionUs: Long + ): MediaPeriod { + val source = currentSource ?: throw IllegalStateException("Source not ready") + val childPeriod = source.createPeriod(id, allocator, startPositionUs) + return TranscodingMediaPeriod(childPeriod, source, id, allocator) + } + + override fun releasePeriod(mediaPeriod: MediaPeriod) { + val transcodingPeriod = mediaPeriod as TranscodingMediaPeriod + transcodingPeriod.release() + + if (transcodingPeriod.currentOffsetUs > 0) { + releaseChildSource(null) + val initialSource = progressiveMediaSourceFactory.createMediaSource(mediaItem) + currentSource = initialSource + prepareChildSource(null, initialSource) + } + } + + override fun getMediaPeriodIdForChildMediaPeriodId( + childSourceId: Void?, + mediaPeriodId: MediaSource.MediaPeriodId + ) = mediaPeriodId + + private inner class TranscodingMediaPeriod( + private var currentPeriod: MediaPeriod, + private var source: MediaSource, + private val id: MediaSource.MediaPeriodId, + private val allocator: Allocator + ) : MediaPeriod, MediaPeriod.Callback { + + private var localCallback: MediaPeriod.Callback? = null + internal var currentOffsetUs: Long = 0 + private var isReloading = false + + private var lastSelections: Array? = null + private var lastMayRetainStreamFlags: BooleanArray? = null + private var activeWrappers: Array = emptyArray() + + fun release() { + source.releasePeriod(currentPeriod) + } + + override fun prepare(callback: MediaPeriod.Callback, positionUs: Long) { + localCallback = callback + currentPeriod.prepare(this, positionUs) + } + + override fun maybeThrowPrepareError() { + if (!isReloading) currentPeriod.maybeThrowPrepareError() + } + + override fun getTrackGroups() = currentPeriod.trackGroups + + override fun getStreamKeys(trackSelections: MutableList) = + currentPeriod.getStreamKeys(trackSelections) + + override fun selectTracks( + selections: Array, + mayRetainStreamFlags: BooleanArray, + streams: Array, + streamResetFlags: BooleanArray, + positionUs: Long + ): Long { + lastSelections = selections + lastMayRetainStreamFlags = mayRetainStreamFlags + + val childStreams = arrayOfNulls(streams.size) + streams.forEachIndexed { i, stream -> + childStreams[i] = (stream as? OffsetSampleStream)?.childStream + } + + val startPos = + currentPeriod.selectTracks( + selections, + mayRetainStreamFlags, + childStreams, + streamResetFlags, + positionUs - currentOffsetUs + ) + + val newWrappers = arrayOfNulls(streams.size) + for (i in streams.indices) { + val child = childStreams[i] + if (child == null) { + streams[i] = null + } else { + val existingWrapper = streams[i] as? OffsetSampleStream + if (existingWrapper != null && existingWrapper.childStream === child) { + newWrappers[i] = existingWrapper + } else { + val wrapper = OffsetSampleStream(child) + newWrappers[i] = wrapper + streams[i] = wrapper + } + } + } + activeWrappers = newWrappers + + return startPos + currentOffsetUs + } + + override fun discardBuffer(positionUs: Long, toKeyframe: Boolean) { + if (!isReloading) { + currentPeriod.discardBuffer(positionUs - currentOffsetUs, toKeyframe) + } + } + + override fun readDiscontinuity(): Long { + if (isReloading) return C.TIME_UNSET + val discontinuity = currentPeriod.readDiscontinuity() + return if (discontinuity == C.TIME_UNSET) C.TIME_UNSET + else discontinuity + currentOffsetUs + } + + override fun seekToUs(positionUs: Long): Long { + if (positionUs == 0L && currentOffsetUs == 0L) { + return currentPeriod.seekToUs(positionUs) + } + + reloadSource(positionUs) + return positionUs + } + + override fun getAdjustedSeekPositionUs(positionUs: Long, seekParameters: SeekParameters) = + positionUs + + override fun getBufferedPositionUs(): Long { + if (isReloading) return currentOffsetUs + val buffered = currentPeriod.bufferedPositionUs + if (buffered == C.TIME_END_OF_SOURCE) return C.TIME_END_OF_SOURCE + return if (buffered == C.TIME_UNSET) C.TIME_UNSET else buffered + currentOffsetUs + } + + override fun getNextLoadPositionUs(): Long { + if (isReloading) return C.TIME_UNSET + val next = currentPeriod.nextLoadPositionUs + if (next == C.TIME_END_OF_SOURCE) return C.TIME_END_OF_SOURCE + return if (next == C.TIME_UNSET) C.TIME_UNSET else next + currentOffsetUs + } + + override fun reevaluateBuffer(positionUs: Long) { + if (!isReloading) currentPeriod.reevaluateBuffer(positionUs - currentOffsetUs) + } + + override fun continueLoading(isLoading: LoadingInfo): Boolean { + if (isReloading) return false + val builder = isLoading.buildUpon() + builder.setPlaybackPositionUs(isLoading.playbackPositionUs - currentOffsetUs) + return currentPeriod.continueLoading(builder.build()) + } + + override fun isLoading() = isReloading || currentPeriod.isLoading + + override fun onPrepared(mediaPeriod: MediaPeriod) { + if (isReloading && mediaPeriod == currentPeriod) { + isReloading = false + restoreTracks() + localCallback?.onContinueLoadingRequested(this) + } else { + localCallback?.onPrepared(this) + } + } + + override fun onContinueLoadingRequested(source: MediaPeriod) { + if (!isReloading) localCallback?.onContinueLoadingRequested(this) + } + + private fun reloadSource(positionUs: Long) { + isReloading = true + currentOffsetUs = positionUs + + activeWrappers.forEach { it?.childStream = null } + + source.releasePeriod(currentPeriod) + releaseChildSource(null) + + val seconds = Util.usToMs(positionUs) / 1000 + val newUri = MusicUtil.getStreamUri(mediaItem.mediaId, seconds.toInt()) + val newMediaItem = mediaItem.buildUpon().setUri(newUri).build() + + val newSource = progressiveMediaSourceFactory.createMediaSource(newMediaItem) + + source = newSource + currentSource = newSource + prepareChildSource(null, newSource) + + val newPeriod = newSource.createPeriod(id, allocator, 0) + currentPeriod = newPeriod + newPeriod.prepare(this, 0) + } + + private fun restoreTracks() { + val selections = lastSelections ?: return + val flags = lastMayRetainStreamFlags ?: return + + val childStreams = arrayOfNulls(activeWrappers.size) + val streamResetFlags = BooleanArray(activeWrappers.size) + + currentPeriod.selectTracks(selections, flags, childStreams, streamResetFlags, 0) + + for (i in activeWrappers.indices) { + activeWrappers[i]?.childStream = childStreams[i] + } + } + + private inner class OffsetSampleStream(var childStream: SampleStream?) : SampleStream { + override fun isReady() = childStream?.isReady ?: false + override fun maybeThrowError() { + childStream?.maybeThrowError() + } + + override fun readData( + formatHolder: FormatHolder, + buffer: DecoderInputBuffer, + readFlags: Int + ): Int { + val stream = childStream ?: return C.RESULT_NOTHING_READ + val result = stream.readData(formatHolder, buffer, readFlags) + if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream) { + buffer.timeUs += currentOffsetUs + } + return result + } + + override fun skipData(positionUs: Long) = + childStream?.skipData(positionUs - currentOffsetUs) ?: 0 + } + } + + private class DurationOverridingTimeline(timeline: Timeline, private val durationUs: Long) : + ForwardingTimeline(timeline) { + + override fun getWindow( + windowIndex: Int, + window: Window, + defaultPositionProjectionUs: Long + ): Window { + super.getWindow(windowIndex, window, defaultPositionProjectionUs) + window.durationUs = durationUs + window.isSeekable = true + window.isDynamic = false + window.liveConfiguration = null + return window + } + + override fun getPeriod(periodIndex: Int, period: Period, setIds: Boolean): Period { + super.getPeriod(periodIndex, period, setIds) + period.durationUs = durationUs + return period + } + } +} From 07b507691cb134484761ae717332b26e395a6380 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sat, 24 Jan 2026 07:51:30 -0800 Subject: [PATCH 5/7] chore: fixed read me donate link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b149fef..56a4eb72 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ The project is a fork of [Tempo](#credits). [Changelog](CHANGELOG.md) [Wiki](USAGE.md) -[Donate](https://github.com/eddyizm/tempus#Support) +[Donate](https://github.com/eddyizm/tempus#donate) **If you find Tempus useful, please consider starring the project on GitHub. It would mean a lot to me and help promote the app to a wider audience.** From d16a9c234f7427915fd1149c19ff8c42780b6ebf Mon Sep 17 00:00:00 2001 From: Pascal Grittmann Date: Sat, 24 Jan 2026 17:39:57 +0100 Subject: [PATCH 6/7] feat: Playback speed controls for music (#376) Enable playback speed controls for music Button is moved to the top left, next to bit rate, because it would overlap with the "shuffle" button. The speed rotation logic was cleaned up to 0.25x increments without all the hard-coded constants and code duplication. Co-authored-by: eddyizm --- .../tempo/ui/activity/MainActivity.java | 2 +- .../ui/fragment/PlayerControllerFragment.java | 36 +++++-------------- .../cappielloantonio/tempo/util/Constants.kt | 7 ---- ...nner_fragment_player_controller_layout.xml | 34 +++++++++--------- 4 files changed, 26 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java b/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java index e0567913..1a75b242 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java @@ -354,7 +354,7 @@ public class MainActivity extends BaseActivity { // TODO Enter all settings to be reset Preferences.setOpenSubsonic(false); - Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_100); + Preferences.setPlaybackSpeed(1.0f); Preferences.setSkipSilenceMode(false); Preferences.setDataSavingMode(false); Preferences.setStarredSyncEnabled(false); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java index e3155b56..f45b5061 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java @@ -413,10 +413,10 @@ public class PlayerControllerFragment extends Fragment { bind.getRoot().setShowNextButton(true); bind.getRoot().setShowFastForwardButton(false); bind.getRoot().setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL | RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE); - bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.GONE); + bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.VISIBLE); bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button).setVisibility(View.GONE); bind.getRoot().findViewById(R.id.button_favorite).setVisibility(View.VISIBLE); - resetPlaybackParameters(mediaBrowser); + setPlaybackParameters(mediaBrowser); break; } } @@ -524,31 +524,11 @@ public class PlayerControllerFragment extends Fragment { playbackSpeedButton.setOnClickListener(view -> { float currentSpeed = Preferences.getPlaybackSpeed(); - if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_080) { - mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_100)); - playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_100)); - Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_100); - } else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_100) { - mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_125)); - playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_125)); - Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_125); - } else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_125) { - mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_150)); - playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_150)); - Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_150); - } else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_150) { - mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_175)); - playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_175)); - Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_175); - } else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_175) { - mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_200)); - playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_200)); - Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_200); - } else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_200) { - mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_080)); - playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_080)); - Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_080); - } + currentSpeed += 0.25f; + if (currentSpeed > 2.0f) currentSpeed = 0.5f; + mediaBrowser.setPlaybackParameters(new PlaybackParameters(currentSpeed)); + playbackSpeedButton.setText(getString(R.string.player_playback_speed, currentSpeed)); + Preferences.setPlaybackSpeed(currentSpeed); }); skipSilenceToggleButton.setOnClickListener(view -> { @@ -600,7 +580,7 @@ public class PlayerControllerFragment extends Fragment { } private void resetPlaybackParameters(MediaBrowser mediaBrowser) { - mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_100)); + mediaBrowser.setPlaybackParameters(new PlaybackParameters(1.0f)); // TODO Resettare lo skip del silenzio } diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt b/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt index 2ae3dbb0..7d2224ed 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/Constants.kt @@ -61,13 +61,6 @@ object Constants { const val MEDIA_TYPE_VIDEO = "video" const val MEDIA_TYPE_RADIO = "radio" - const val MEDIA_PLAYBACK_SPEED_080 = 0.8f - const val MEDIA_PLAYBACK_SPEED_100 = 1.0f - const val MEDIA_PLAYBACK_SPEED_125 = 1.25f - const val MEDIA_PLAYBACK_SPEED_150 = 1.50f - const val MEDIA_PLAYBACK_SPEED_175 = 1.75f - const val MEDIA_PLAYBACK_SPEED_200 = 2.0f - const val MEDIA_RECENTLY_PLAYED = "MEDIA_RECENTLY_PLAYED" const val MEDIA_MOST_PLAYED = "MEDIA_MOST_PLAYED" const val MEDIA_RECENTLY_ADDED = "MEDIA_RECENTLY_ADDED" diff --git a/app/src/main/res/layout/inner_fragment_player_controller_layout.xml b/app/src/main/res/layout/inner_fragment_player_controller_layout.xml index 29747587..8fab7c84 100644 --- a/app/src/main/res/layout/inner_fragment_player_controller_layout.xml +++ b/app/src/main/res/layout/inner_fragment_player_controller_layout.xml @@ -16,6 +16,23 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> +