diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt index 3e95ef631..23e9bf40c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.kt +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt @@ -155,3 +155,5 @@ const val WHITELIST_MUSIC = "whitelist_music" const val MATERIAL_YOU = "material_you" const val SNOWFALL = "snowfall" const val LYRICS_TYPE = "lyrics_type" +const val PLAYBACK_SPEED = "playback_speed" +const val PLAYBACK_PITCH = "playback_pitch" diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/PlaybackSpeedDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/PlaybackSpeedDialog.kt new file mode 100644 index 000000000..114915fa0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/PlaybackSpeedDialog.kt @@ -0,0 +1,52 @@ +package code.name.monkey.retromusic.dialogs + +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.databinding.DialogPlaybackSpeedBinding +import code.name.monkey.retromusic.extensions.accent +import code.name.monkey.retromusic.extensions.colorButtons +import code.name.monkey.retromusic.extensions.materialDialog +import code.name.monkey.retromusic.util.PreferenceUtil +import com.google.android.material.slider.Slider + +class PlaybackSpeedDialog : DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val binding = DialogPlaybackSpeedBinding.inflate(layoutInflater) + binding.playbackSpeedSlider.accent() + binding.playbackPitchSlider.accent() + binding.playbackSpeedSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> + binding.speedValue.text = "$value" + }) + binding.playbackPitchSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> + binding.pitchValue.text = "$value" + }) + binding.playbackSpeedSlider.value = PreferenceUtil.playbackSpeed + binding.playbackPitchSlider.value = PreferenceUtil.playbackPitch + + return materialDialog(R.string.playback_settings) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.save) { _, _ -> + updatePlaybackAndPitch( + binding.playbackSpeedSlider.value, + binding.playbackPitchSlider.value + ) + } + .setView(binding.root) + .create() + .colorButtons() + } + + private fun updatePlaybackAndPitch(speed: Float, pitch: Float) { + PreferenceUtil.playbackSpeed = speed + PreferenceUtil.playbackPitch = pitch + } + + companion object { + fun newInstance(): PlaybackSpeedDialog { + return PlaybackSpeedDialog() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt index 41c4f70de..6463070d7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt @@ -43,6 +43,7 @@ import com.google.android.material.button.MaterialButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.progressindicator.CircularProgressIndicator +import com.google.android.material.slider.Slider import com.google.android.material.textfield.TextInputLayout fun Int.ripAlpha(): Int { @@ -112,6 +113,12 @@ fun SeekBar.addAccentColor() { thumbTintList = colorState } +fun Slider.accent() { + if (materialYou) return + trackActiveTintList = context.accentColor().colorStateList + trackInactiveTintList = context.accentColorVariant().colorStateList +} + fun Button.accentTextColor() { if (materialYou) return setTextColor(ThemeStore.accentColor(App.getContext())) @@ -269,6 +276,15 @@ fun Context.darkAccentColorVariant(): Int { ) } +@ColorInt +fun Context.accentColorVariant(): Int { + return if (surfaceColor().isColorLight) { + accentColor().darkerColor + } else { + accentColor().lighterColor + } +} + inline val @receiver:ColorInt Int.isColorLight get() = ColorUtil.isColorLight(this) @@ -276,4 +292,7 @@ inline val @receiver:ColorInt Int.lighterColor get() = ColorUtil.lightenColor(this) inline val @receiver:ColorInt Int.darkerColor - get() = ColorUtil.darkenColor(this) \ No newline at end of file + get() = ColorUtil.darkenColor(this) + +inline val Int.colorStateList : ColorStateList + get() = ColorStateList.valueOf(this) \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt index bbbc8dd2a..eba65a37a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt @@ -42,6 +42,7 @@ import androidx.viewpager.widget.ViewPager import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.EXTRA_ARTIST_ID +import code.name.monkey.retromusic.PLAYBACK_SPEED import code.name.monkey.retromusic.R import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity @@ -79,6 +80,10 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme ): Boolean { val song = MusicPlayerRemote.currentSong when (item.itemId) { + R.id.action_playback_speed -> { + PlaybackSpeedDialog.newInstance().show(childFragmentManager, "PLAYBACK_SETTINGS") + return true + } R.id.action_toggle_lyrics -> { PreferenceUtil.showLyrics = !item.isChecked item.isChecked = !item.isChecked diff --git a/app/src/main/java/code/name/monkey/retromusic/preferences/DurationPreference.kt b/app/src/main/java/code/name/monkey/retromusic/preferences/DurationPreference.kt index c9b5e5d2b..e0581896c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/preferences/DurationPreference.kt +++ b/app/src/main/java/code/name/monkey/retromusic/preferences/DurationPreference.kt @@ -1,17 +1,3 @@ -/* - * Copyright (c) 2019 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This 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 3 of the License, or (at your option) any later version. - * - * This software 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. - */ - package code.name.monkey.retromusic.preferences import android.app.Dialog diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java index a861543d3..c3768dc17 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java @@ -16,8 +16,10 @@ package code.name.monkey.retromusic.service; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.media.AudioManager; import android.media.MediaPlayer; +import android.media.PlaybackParams; import android.media.audiofx.AudioEffect; import android.net.Uri; import android.os.PowerManager; @@ -26,7 +28,10 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.preference.PreferenceManager; +import code.name.monkey.appthemehelper.util.VersionUtils; +import code.name.monkey.retromusic.ConstantsKt; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.service.playback.Playback; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -35,7 +40,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil; * @author Andrew Neal, Karim Abou Zeid (kabouzeid) */ public class MultiPlayer - implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { + implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener { public static final String TAG = MultiPlayer.class.getSimpleName(); private MediaPlayer mCurrentMediaPlayer = new MediaPlayer(); @@ -53,6 +58,7 @@ public class MultiPlayer MultiPlayer(final Context context) { this.context = context; mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); + PreferenceManager.getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(this); } /** @@ -86,6 +92,7 @@ public class MultiPlayer } else { player.setDataSource(path); } + setPlaybackSpeedPitch(player); player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.prepare(); } catch (Exception e) { @@ -199,6 +206,7 @@ public class MultiPlayer if (mNextMediaPlayer != null) { mNextMediaPlayer.release(); } + PreferenceManager.getDefaultSharedPreferences(context).unregisterOnSharedPreferenceChangeListener(this); } /** @@ -346,4 +354,23 @@ public class MultiPlayer @Override public void setCrossFadeDuration(int duration) { } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key.equals(ConstantsKt.PLAYBACK_SPEED) || key.equals(ConstantsKt.PLAYBACK_PITCH)) { + setPlaybackSpeedPitch(mCurrentMediaPlayer); + } + } + + public void setPlaybackSpeedPitch(MediaPlayer mp) { + if (VersionUtils.INSTANCE.hasMarshmallow()) { + boolean wasPlaying = mp.isPlaying(); + mp.setPlaybackParams(new PlaybackParams() + .setSpeed(PreferenceUtil.INSTANCE.getPlaybackSpeed()) + .setPitch(PreferenceUtil.INSTANCE.getPlaybackPitch())); + if (!wasPlaying) { + if (mp.isPlaying()) mp.pause(); + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java index d6add80cc..9cd954f80 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java @@ -22,6 +22,7 @@ import static code.name.monkey.retromusic.ConstantsKt.BLURRED_ALBUM_ART; import static code.name.monkey.retromusic.ConstantsKt.CLASSIC_NOTIFICATION; import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION; import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION; +import static code.name.monkey.retromusic.ConstantsKt.PLAYBACK_SPEED; import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET; import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator; @@ -940,6 +941,9 @@ public class MusicService extends MediaBrowserServiceCompat playingNotification.setPlaying(isPlaying()); playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify); break; + case PLAYBACK_SPEED: + updateMediaSessionPlaybackState(); + break; case TOGGLE_HEADSET: registerHeadsetEvents(); break; @@ -1334,7 +1338,7 @@ public class MusicService extends MediaBrowserServiceCompat .setState( isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED, getSongProgressMillis(), - 1); + PreferenceUtil.INSTANCE.getPlaybackSpeed()); setCustomAction(stateBuilder); diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt index f310bb9cc..7a92d61a6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt @@ -674,6 +674,16 @@ object PreferenceUtil { } else { LyricsType.OVER_LYRICS } + + var playbackSpeed + get() = sharedPreferences + .getFloat(PLAYBACK_SPEED, 1F) + set(value) = sharedPreferences.edit { putFloat(PLAYBACK_SPEED, value) } + + var playbackPitch + get() = sharedPreferences + .getFloat(PLAYBACK_PITCH, 1F) + set(value) = sharedPreferences.edit { putFloat(PLAYBACK_PITCH, value) } } enum class LyricsType { REPLACE_LYRICS, OVER_LYRICS diff --git a/app/src/main/res/drawable/ic_playback_speed.xml b/app/src/main/res/drawable/ic_playback_speed.xml new file mode 100644 index 000000000..39b481679 --- /dev/null +++ b/app/src/main/res/drawable/ic_playback_speed.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/dialog_playback_speed.xml b/app/src/main/res/layout/dialog_playback_speed.xml new file mode 100644 index 000000000..227251cec --- /dev/null +++ b/app/src/main/res/layout/dialog_playback_speed.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_player.xml b/app/src/main/res/menu/menu_player.xml index 948fc66fb..ee9aaa967 100755 --- a/app/src/main/res/menu/menu_player.xml +++ b/app/src/main/res/menu/menu_player.xml @@ -1,6 +1,15 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + You have to select at least one category. You will be forwarded to the issue tracker website. Your account data is only used for authentication. + Playback Speed + Pitch + Playback Settings