feat: Add audio equalizer with UI

This commit is contained in:
Jaime García 2025-09-08 19:28:34 +02:00
parent c62d2ace4d
commit 7c0d44680f
No known key found for this signature in database
GPG key ID: BC4E5F71A71BDA5B
26 changed files with 762 additions and 31 deletions

View file

@ -0,0 +1,47 @@
package com.cappielloantonio.tempo.service
import android.media.audiofx.Equalizer
class EqualizerManager {
private var equalizer: Equalizer? = null
fun attachToSession(audioSessionId: Int): Boolean {
release()
if (audioSessionId != 0 && audioSessionId != -1) {
try {
equalizer = Equalizer(0, audioSessionId).apply {
enabled = true
}
return true
} catch (e: Exception) {
// Some devices may not support Equalizer or audio session may be invalid
equalizer = null
}
}
return false
}
fun setBandLevel(band: Short, level: Short) {
equalizer?.setBandLevel(band, level)
}
fun getNumberOfBands(): Short = equalizer?.numberOfBands ?: 0
fun getBandLevelRange(): ShortArray? = equalizer?.bandLevelRange
fun getCenterFreq(band: Short): Int? =
equalizer?.getCenterFreq(band)?.div(1000)
fun getBandLevel(band: Short): Short? =
equalizer?.getBandLevel(band)
fun setEnabled(enabled: Boolean) {
equalizer?.enabled = enabled
}
fun release() {
equalizer?.release()
equalizer = null
}
}

View file

@ -0,0 +1,225 @@
package com.cappielloantonio.tempo.ui.fragment
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.annotation.OptIn
import androidx.fragment.app.Fragment
import androidx.media3.common.util.UnstableApi
import com.cappielloantonio.tempo.R
import com.cappielloantonio.tempo.service.EqualizerManager
import com.cappielloantonio.tempo.service.MediaService
import com.cappielloantonio.tempo.util.Preferences
class EqualizerFragment : Fragment() {
private var equalizerManager: EqualizerManager? = null
private lateinit var eqBandsContainer: LinearLayout
private lateinit var eqSwitch: Switch
private lateinit var resetButton: Button
private lateinit var safeSpace: Space
private val bandSeekBars = mutableListOf<SeekBar>()
private val connection = object : ServiceConnection {
@OptIn(UnstableApi::class)
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as MediaService.LocalBinder
equalizerManager = binder.getEqualizerManager()
initUI()
restoreEqualizerPreferences()
}
override fun onServiceDisconnected(arg0: ComponentName) {
equalizerManager = null
}
}
@OptIn(UnstableApi::class)
override fun onStart() {
super.onStart()
Intent(requireContext(), MediaService::class.java).also { intent ->
intent.action = MediaService.ACTION_BIND_EQUALIZER
requireActivity().bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
requireActivity().unbindService(connection)
equalizerManager = null
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_equalizer, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
eqBandsContainer = view.findViewById(R.id.eq_bands_container)
eqSwitch = view.findViewById(R.id.equalizer_switch)
resetButton = view.findViewById(R.id.equalizer_reset_button)
safeSpace = view.findViewById(R.id.equalizer_bottom_space)
}
private fun initUI() {
val manager = equalizerManager
val notSupportedView = view?.findViewById<LinearLayout>(R.id.equalizer_not_supported_container)
val switchRow = view?.findViewById<View>(R.id.equalizer_switch_row)
if (manager == null || manager.getNumberOfBands().toInt() == 0) {
switchRow?.visibility = View.GONE
resetButton.visibility = View.GONE
eqBandsContainer.visibility = View.GONE
safeSpace.visibility = View.GONE
notSupportedView?.visibility = View.VISIBLE
return
}
notSupportedView?.visibility = View.GONE
switchRow?.visibility = View.VISIBLE
resetButton.visibility = View.VISIBLE
eqBandsContainer.visibility = View.VISIBLE
safeSpace.visibility = View.VISIBLE
eqSwitch.setOnCheckedChangeListener(null)
eqSwitch.isChecked = Preferences.isEqualizerEnabled()
updateUiEnabledState(eqSwitch.isChecked)
eqSwitch.setOnCheckedChangeListener { _, isChecked ->
manager.setEnabled(isChecked)
Preferences.setEqualizerEnabled(isChecked)
updateUiEnabledState(isChecked)
}
createBandSliders()
resetButton.setOnClickListener {
resetEqualizer()
saveBandLevelsToPreferences()
}
}
private fun updateUiEnabledState(isEnabled: Boolean) {
resetButton.isEnabled = isEnabled
bandSeekBars.forEach { it.isEnabled = isEnabled }
}
private fun createBandSliders() {
val manager = equalizerManager ?: return
eqBandsContainer.removeAllViews()
bandSeekBars.clear()
val bands = manager.getNumberOfBands()
val bandLevelRange = manager.getBandLevelRange() ?: shortArrayOf(-1500, 1500)
val minLevel = bandLevelRange[0].toInt()
val maxLevel = bandLevelRange[1].toInt()
val savedLevels = Preferences.getEqualizerBandLevels(bands)
for (i in 0 until bands) {
val band = i.toShort()
val freq = manager.getCenterFreq(band) ?: 0
val row = LinearLayout(requireContext()).apply {
orientation = LinearLayout.HORIZONTAL
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
topMargin = 24
bottomMargin = 24
}
setPadding(0, 8, 0, 8)
}
val freqLabel = TextView(requireContext(), null, 0, R.style.LabelSmall).apply {
text = if (freq >= 1000) {
if (freq % 1000 == 0) {
"${freq / 1000} kHz"
} else {
String.format("%.1f kHz", freq / 1000f)
}
} else {
"$freq Hz"
}
width = 120
}
row.addView(freqLabel)
val initialLevel = savedLevels.getOrNull(i) ?: (manager.getBandLevel(band)?.toInt() ?: 0)
val dbLabel = TextView(requireContext(), null, 0, R.style.LabelSmall).apply {
text = "${(initialLevel.toInt() / 100)} dB"
setPadding(12, 0, 0, 0)
width = 120
gravity = Gravity.END
}
val seekBar = SeekBar(requireContext()).apply {
max = maxLevel - minLevel
progress = initialLevel.toInt() - minLevel
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
val thisLevel = (progress + minLevel).toShort()
if (fromUser) {
manager.setBandLevel(band, thisLevel)
saveBandLevelsToPreferences()
}
dbLabel.text = "${((progress + minLevel) / 100)} dB"
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
bandSeekBars.add(seekBar)
row.addView(seekBar)
row.addView(dbLabel)
eqBandsContainer.addView(row)
}
}
private fun resetEqualizer() {
val manager = equalizerManager ?: return
val bands = manager.getNumberOfBands()
val bandLevelRange = manager.getBandLevelRange() ?: shortArrayOf(-1500, 1500)
val minLevel = bandLevelRange[0].toInt()
val midLevel = 0
for (i in 0 until bands) {
manager.setBandLevel(i.toShort(), midLevel.toShort())
bandSeekBars.getOrNull(i)?.progress = midLevel - minLevel
}
Preferences.setEqualizerBandLevels(ShortArray(bands.toInt()))
}
private fun saveBandLevelsToPreferences() {
val manager = equalizerManager ?: return
val bands = manager.getNumberOfBands()
val levels = ShortArray(bands.toInt()) { i -> manager.getBandLevel(i.toShort()) ?: 0 }
Preferences.setEqualizerBandLevels(levels)
}
private fun restoreEqualizerPreferences() {
val manager = equalizerManager ?: return
eqSwitch.isChecked = Preferences.isEqualizerEnabled()
updateUiEnabledState(eqSwitch.isChecked)
val bands = manager.getNumberOfBands()
val bandLevelRange = manager.getBandLevelRange() ?: shortArrayOf(-1500, 1500)
val minLevel = bandLevelRange[0].toInt()
val savedLevels = Preferences.getEqualizerBandLevels(bands)
if (savedLevels != null) {
for (i in 0 until bands) {
manager.setBandLevel(i.toShort(), savedLevels[i])
bandSeekBars.getOrNull(i)?.progress = savedLevels[i] - minLevel
}
}
}
}

View file

@ -24,6 +24,8 @@ import androidx.media3.common.util.RepeatModeUtil;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaBrowser; import androidx.media3.session.MediaBrowser;
import androidx.media3.session.SessionToken; import androidx.media3.session.SessionToken;
import androidx.navigation.NavController;
import androidx.navigation.NavOptions;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.viewpager2.widget.ViewPager2; import androidx.viewpager2.widget.ViewPager2;
@ -68,6 +70,7 @@ public class PlayerControllerFragment extends Fragment {
private ImageButton playerOpenQueueButton; private ImageButton playerOpenQueueButton;
private ImageButton playerTrackInfo; private ImageButton playerTrackInfo;
private LinearLayout ratingContainer; private LinearLayout ratingContainer;
private ImageButton equalizerButton;
private MainActivity activity; private MainActivity activity;
private PlayerBottomSheetViewModel playerBottomSheetViewModel; private PlayerBottomSheetViewModel playerBottomSheetViewModel;
@ -89,6 +92,7 @@ public class PlayerControllerFragment extends Fragment {
initMediaListenable(); initMediaListenable();
initMediaLabelButton(); initMediaLabelButton();
initArtistLabelButton(); initArtistLabelButton();
initEqualizerButton();
return view; return view;
} }
@ -126,6 +130,7 @@ public class PlayerControllerFragment extends Fragment {
playerTrackInfo = bind.getRoot().findViewById(R.id.player_info_track); playerTrackInfo = bind.getRoot().findViewById(R.id.player_info_track);
songRatingBar = bind.getRoot().findViewById(R.id.song_rating_bar); songRatingBar = bind.getRoot().findViewById(R.id.song_rating_bar);
ratingContainer = bind.getRoot().findViewById(R.id.rating_container); ratingContainer = bind.getRoot().findViewById(R.id.rating_container);
equalizerButton = bind.getRoot().findViewById(R.id.player_open_equalizer_button);
checkAndSetRatingContainerVisibility(); checkAndSetRatingContainerVisibility();
} }
@ -426,6 +431,18 @@ public class PlayerControllerFragment extends Fragment {
}); });
} }
private void initEqualizerButton() {
equalizerButton.setOnClickListener(v -> {
NavController navController = NavHostFragment.findNavController(this);
NavOptions navOptions = new NavOptions.Builder()
.setLaunchSingleTop(true)
.setPopUpTo(R.id.equalizerFragment, true)
.build();
navController.navigate(R.id.equalizerFragment, null, navOptions);
if (activity != null) activity.collapseBottomSheetDelayed();
});
}
public void goToControllerPage() { public void goToControllerPage() {
playerMediaCoverViewPager.setCurrentItem(0, false); playerMediaCoverViewPager.setCurrentItem(0, false);
} }

View file

@ -18,6 +18,9 @@ import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.os.LocaleListCompat; import androidx.core.os.LocaleListCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.navigation.NavController;
import androidx.navigation.NavOptions;
import androidx.navigation.fragment.NavHostFragment;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
@ -86,7 +89,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
checkEqualizer(); checkSystemEqualizer();
checkCacheStorage(); checkCacheStorage();
checkStorage(); checkStorage();
@ -102,6 +105,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
actionChangeDownloadStorage(); actionChangeDownloadStorage();
actionDeleteDownloadStorage(); actionDeleteDownloadStorage();
actionKeepScreenOn(); actionKeepScreenOn();
actionAppEqualizer();
} }
@Override @Override
@ -124,8 +128,8 @@ public class SettingsFragment extends PreferenceFragmentCompat {
} }
} }
private void checkEqualizer() { private void checkSystemEqualizer() {
Preference equalizer = findPreference("equalizer"); Preference equalizer = findPreference("system_equalizer");
if (equalizer == null) return; if (equalizer == null) return;
@ -353,4 +357,21 @@ public class SettingsFragment extends PreferenceFragmentCompat {
return true; return true;
}); });
} }
private void actionAppEqualizer() {
Preference appEqualizer = findPreference("app_equalizer");
if (appEqualizer != null) {
appEqualizer.setOnPreferenceClickListener(preference -> {
NavController navController = NavHostFragment.findNavController(this);
NavOptions navOptions = new NavOptions.Builder()
.setLaunchSingleTop(true)
.setPopUpTo(R.id.equalizerFragment, true)
.build();
activity.setBottomNavigationBarVisibility(true);
activity.setBottomSheetVisibility(true);
navController.navigate(R.id.equalizerFragment, null, navOptions);
return true;
});
}
}
} }

View file

@ -69,7 +69,8 @@ object Preferences {
private const val NEXT_UPDATE_CHECK = "next_update_check" private const val NEXT_UPDATE_CHECK = "next_update_check"
private const val CONTINUOUS_PLAY = "continuous_play" private const val CONTINUOUS_PLAY = "continuous_play"
private const val LAST_INSTANT_MIX = "last_instant_mix" private const val LAST_INSTANT_MIX = "last_instant_mix"
private const val EQUALIZER_ENABLED = "equalizer_enabled"
private const val EQUALIZER_BAND_LEVELS = "equalizer_band_levels"
@JvmStatic @JvmStatic
fun getServer(): String? { fun getServer(): String? {
@ -538,4 +539,31 @@ object Preferences {
LAST_INSTANT_MIX, 0 LAST_INSTANT_MIX, 0
) + 5000 < System.currentTimeMillis() ) + 5000 < System.currentTimeMillis()
} }
@JvmStatic
fun setEqualizerEnabled(enabled: Boolean) {
App.getInstance().preferences.edit().putBoolean(EQUALIZER_ENABLED, enabled).apply()
}
@JvmStatic
fun isEqualizerEnabled(): Boolean {
return App.getInstance().preferences.getBoolean(EQUALIZER_ENABLED, false)
}
@JvmStatic
fun setEqualizerBandLevels(bandLevels: ShortArray) {
val asString = bandLevels.joinToString(",")
App.getInstance().preferences.edit().putString(EQUALIZER_BAND_LEVELS, asString).apply()
}
@JvmStatic
fun getEqualizerBandLevels(bandCount: Short): ShortArray {
val str = App.getInstance().preferences.getString(EQUALIZER_BAND_LEVELS, null)
if (str.isNullOrBlank()) {
return ShortArray(bandCount.toInt())
}
val parts = str.split(",")
if (parts.size < bandCount) return ShortArray(bandCount.toInt())
return ShortArray(bandCount.toInt()) { i -> parts[i].toShortOrNull() ?: 0 }
}
} }

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:autoMirrored="true">
<path
android:fillColor="@color/titleTextColor"
android:pathData="M160,800L160,480L320,480L320,800L160,800ZM400,800L400,160L560,160L560,800L400,800ZM640,800L640,360L800,360L800,800L640,800Z"/>
</vector>

View file

@ -0,0 +1,93 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="757.96dp"
android:height="743.73dp"
android:viewportWidth="757.96"
android:viewportHeight="743.73">
<path
android:pathData="M91.45,0a32.04,32.04 0,0 0,-32 32L59.45,710.43a32.04,32.04 0,0 0,32 32h297a32.04,32.04 0,0 0,32 -32L420.45,32a32.04,32.04 0,0 0,-32 -32Z"
android:fillColor="#e6e6e6"/>
<path
android:pathData="M400.66,156.98v-54.44a125.25,125.25 0,0 1,-80.86 -60.19h0a23.79,23.79 0,0 1,-14.22 4.68L262.35,47.03A178.55,178.55 0,0 0,400.66 156.98Z"
android:fillColor="#fff"/>
<path
android:pathData="M400.66,99.42v-52.3a29.12,29.12 0,0 0,-29.13 -29.13h-41.97v5.05a23.92,23.92 0,0 1,-7.4 17.33,122.3 122.3,0 0,0 78.5,59.05Z"
android:fillColor="#fff"/>
<path
android:pathData="M198.77,47.03L171.74,47.03a23.99,23.99 0,0 1,-23.98 -23.99v-5.05L108.38,17.99a29.13,29.13 0,0 0,-29.13 29.13v648.2a29.08,29.08 0,0 0,29.13 29.11h263.15a28.36,28.36 0,0 0,3.59 -0.22,29.15 29.15,0 0,0 25.54,-28.89L400.66,218.15C304.95,207.07 225.2,138.77 198.77,47.03Z"
android:fillColor="#fff"/>
<path
android:pathData="M259.07,47.03h-57.14c26.3,90.04 104.68,157.03 198.73,168.07v-55.02A181.67,181.67 0,0 1,259.07 47.03Z"
android:fillColor="#fff"/>
<path
android:pathData="M380.61,532.78h-270a5.01,5.01 0,0 1,-5 -5L105.61,460.81a5.01,5.01 0,0 1,5 -5h270a5.01,5.01 0,0 1,5 5v66.98A5.01,5.01 0,0 1,380.61 532.78ZM110.61,457.8a3,3 0,0 0,-3 3v66.98a3,3 0,0 0,3 3h270a3,3 0,0 0,3 -3L383.61,460.81a3,3 0,0 0,-3 -3Z"
android:fillColor="#e6e6e6"/>
<path
android:pathData="M145.61,494.29m-21,0a21,21 0,1 1,42 0a21,21 0,1 1,-42 0"
android:fillColor="#3f3d56"/>
<path
android:pathData="M194.11,480.29a3.5,3.5 0,0 0,0 7h165a3.5,3.5 0,1 0,0 -7Z"
android:fillColor="#e6e6e6"/>
<path
android:pathData="M194.11,501.29a3.5,3.5 0,0 0,0 7h165a3.5,3.5 0,1 0,0 -7Z"
android:fillColor="#e6e6e6"/>
<path
android:pathData="M380.61,644.78h-270a5.01,5.01 0,0 1,-5 -5L105.61,572.81a5.01,5.01 0,0 1,5 -5h270a5.01,5.01 0,0 1,5 5v66.98A5.01,5.01 0,0 1,380.61 644.78ZM110.61,569.8a3,3 0,0 0,-3 3v66.98a3,3 0,0 0,3 3h270a3,3 0,0 0,3 -3L383.61,572.81a3,3 0,0 0,-3 -3Z"
android:fillColor="#e6e6e6"/>
<path
android:pathData="M145.61,606.29m-21,0a21,21 0,1 1,42 0a21,21 0,1 1,-42 0"
android:fillColor="#3f3d56"/>
<path
android:pathData="M194.11,592.29a3.5,3.5 0,0 0,0 7h165a3.5,3.5 0,1 0,0 -7Z"
android:fillColor="#e6e6e6"/>
<path
android:pathData="M194.11,613.29a3.5,3.5 0,0 0,0 7h165a3.5,3.5 0,1 0,0 -7Z"
android:fillColor="#e6e6e6"/>
<path
android:pathData="M239.93,394a94.96,94.96 0,0 1,-95 -95c0,-0.2 0,-0.41 0.01,-0.61 0.29,-52.03 42.9,-94.39 94.99,-94.39a95,95 0,1 1,0 190ZM239.93,206a93.2,93.2 0,0 0,-92.99 92.46c-0.01,0.21 -0.01,0.38 -0.01,0.54a93.01,93.01 0,1 0,93 -93Z"
android:fillColor="#3f3d56"/>
<path
android:pathData="M282.95,296.81l-65.02,-37.54a2,2 0,0 0,-3 1.73L214.93,336.08a2,2 0,0 0,3 1.73l65.02,-37.54a2,2 0,0 0,0 -3.46l-65.02,-37.54a2,2 0,0 0,-3 1.73L214.93,336.08a2,2 0,0 0,3 1.73l65.02,-37.54a2,2 0,0 0,0 -3.46Z"
android:fillColor="#6c63ff"/>
<path
android:pathData="M757.57,743.73H0v-2.18H757.96Z"
android:fillColor="#3f3d56"/>
<path
android:pathData="M590.68,338.14m-27.94,0a27.94,27.94 0,1 1,55.87 0a27.94,27.94 0,1 1,-55.87 0"
android:fillColor="#ffb8b8"/>
<path
android:pathData="M588.87,494.75a12.51,12.51 0,0 1,9.47 -16.1,11.89 11.89,0 0,1 1.66,-0.2l29.43,-47.23L602.55,405.66A10.73,10.73 0,1 1,617.47 390.25l37.11,36.6 0.08,0.09a9.72,9.72 0,0 1,-0.68 11.58L612.75,487.28a11.73,11.73 0,0 1,0.31 1.19,12.51 12.51,0 0,1 -11.23,14.92q-0.53,0.05 -1.06,0.05A12.55,12.55 0,0 1,588.87 494.75Z"
android:fillColor="#ffb8b8"/>
<path
android:pathData="M544.67,726.93L530.72,726.93l-6.63,-53.79 20.58,0Z"
android:fillColor="#ffb8b8"/>
<path
android:pathData="M548.79,741.02l-46.1,0L502.69,739.88a18.07,18.07 0,0 1,18.07 -18.07h28.03Z"
android:fillColor="#2f2e41"/>
<path
android:pathData="M683.27,707.66l-11.98,7.14 -33.22,-42.82 17.68,-10.53Z"
android:fillColor="#ffb8b8"/>
<path
android:pathData="M654.41,741.23l-0.58,-0.98a18.07,18.07 0,0 1,6.28 -24.77l24.08,-14.34 9.83,16.5Z"
android:fillColor="#2f2e41"/>
<path
android:pathData="M522.33,703.25c-9.34,-109.99 -14.9,-212.18 19.25,-253.86l0.26,-0.32 57.47,22.99 0.09,0.2c0.19,0.42 19.31,42.46 14.85,70.74l14.18,65.21 46.22,77.39a5.12,5.12 0,0 1,-2.33 7.31l-20.09,8.84a5.14,5.14 0,0 1,-6.42 -2.01L595.53,617.75l-28.4,-62.88a1.71,1.71 0,0 0,-3.25 0.52L548.14,703.36a5.11,5.11 0,0 1,-5.09 4.58L527.43,707.94A5.15,5.15 0,0 1,522.33 703.25Z"
android:fillColor="#2f2e41"/>
<path
android:pathData="M541.77,450.26l-0.27,-0.13 -0.04,-0.3c-2.15,-15.02 0.39,-31.72 7.55,-49.62a39.4,39.4 0,0 1,45.73 -23.59h0a39.35,39.35 0,0 1,25.09 19.3,38.92 38.92,0 0,1 2.7,31.19c-9.02,26.39 -20.73,51.08 -20.85,51.32l-0.25,0.51Z"
android:fillColor="#6c63ff"/>
<path
android:pathData="M500.42,512.57a12.78,12.78 0,0 1,9.16 -13.94l53.74,-103.17a10.3,10.3 0,1 1,17.52 10.82L525.84,508.73a12.42,12.42 0,0 1,0.2 1.89,12.86 12.86,0 0,1 -13.03,13.21h0a12.87,12.87 0,0 1,-9.87 -4.83,12.71 12.71,0 0,1 -2.71,-6.43Z"
android:fillColor="#ffb8b8"/>
<path
android:pathData="M556.81,322.35h44.36L601.16,303.02c-9.74,-3.87 -19.26,-7.16 -25.02,0a19.34,19.34 0,0 0,-19.34 19.34Z"
android:fillColor="#2f2e41"/>
<path
android:pathData="M603.62,299.61c26.52,0 33.94,33.24 33.94,51.99 0,10.46 -4.73,14.2 -12.16,15.46l-2.63,-14 -6.15,14.6c-2.09,0.01 -4.28,-0.03 -6.55,-0.07l-2.08,-4.29 -4.65,4.22c-18.62,0.03 -33.66,2.74 -33.66,-15.92C569.68,332.85 576.19,299.61 603.62,299.61Z"
android:fillColor="#2f2e41"/>
<path
android:pathData="M595.72,327L595.72,301.13a2.33,2.33 0,0 0,-2.33 -2.33h-4.67a2.33,2.33 0,0 0,-2.33 2.33L586.39,325.44a14.74,14.74 0,1 0,9.33 1.56Z"
android:fillColor="#6c63ff"/>
<path
android:pathData="M589.5,340.01m-7,0a7,7 0,1 1,14 0a7,7 0,1 1,-14 0"
android:fillColor="#fff"/>
</vector>

View file

@ -382,11 +382,23 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp" android:padding="16dp"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toEndOf="@+id/player_open_equalizer_button"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/ic_queue" /> app:srcCompat="@drawable/ic_queue" />
<ImageButton
android:id="@+id/player_open_equalizer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/player_open_queue_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/ic_eq" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/eq_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/eq_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:padding="16dp">
<LinearLayout
android:id="@+id/eq_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/equalizer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/equalizer_fragment_title"
style="@style/HeadlineSmall"
android:layout_gravity="center_horizontal"
android:paddingBottom="16dp" />
<LinearLayout
android:id="@+id/equalizer_switch_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingBottom="16dp">
<TextView
android:id="@+id/equalizer_switch_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
style="@style/LabelMedium"
android:text="@string/equalizer_enable" />
<Switch
android:id="@+id/equalizer_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/eq_bands_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
<Button
android:id="@+id/equalizer_reset_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/equalizer_reset"
android:layout_gravity="center_horizontal"
style="@style/Widget.Material3.Button.TextButton"
android:layout_marginTop="24dp"/>
<Space
android:id="@+id/equalizer_bottom_space"
android:layout_width="match_parent"
android:layout_height="128dp"
android:layout_marginTop="0dp" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/equalizer_not_supported_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:layout_gravity="center"
android:visibility="gone">
<ImageView
android:id="@+id/equalizer_not_supported_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:maxWidth="240dp"
android:maxHeight="240dp"
android:scaleType="centerInside"
android:src="@drawable/ui_eq_not_supported" />
<TextView
android:id="@+id/equalizer_not_supported_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/equalizer_not_supported"
android:gravity="center"
style="@style/BodyMedium"
android:layout_marginTop="16dp"/>
</LinearLayout>
</FrameLayout>

View file

@ -381,11 +381,23 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp" android:padding="16dp"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toEndOf="@+id/player_open_equalizer_button"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/ic_queue" /> app:srcCompat="@drawable/ic_queue" />
<ImageButton
android:id="@+id/player_open_equalizer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/player_open_queue_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/ic_eq" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -151,6 +151,9 @@
app:destination="@id/loginFragment" app:destination="@id/loginFragment"
app:popUpTo="@id/homeFragment" app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" /> app:popUpToInclusive="true" />
<action
android:id="@+id/action_settingsFragment_to_equalizerFragment"
app:destination="@id/equalizerFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/searchFragment" android:id="@+id/searchFragment"
@ -300,6 +303,20 @@
android:id="@+id/action_indexFragment_to_directoryFragment" android:id="@+id/action_indexFragment_to_directoryFragment"
app:destination="@id/directoryFragment" /> app:destination="@id/directoryFragment" />
</fragment> </fragment>
<fragment
android:id="@+id/playerControllerFragment"
android:name="com.cappielloantonio.tempo.ui.fragment.PlayerControllerFragment"
android:label="PlayerControllerFragment"
tools:layout="@layout/inner_fragment_player_controller">
<action
android:id="@+id/action_playerControllerFragment_to_equalizerFragment"
app:destination="@id/equalizerFragment"/>
</fragment>
<fragment
android:id="@+id/equalizerFragment"
android:name="com.cappielloantonio.tempo.ui.fragment.EqualizerFragment"
android:label="EqualizerFragment"
tools:layout="@layout/fragment_equalizer" />
<dialog <dialog
android:id="@+id/songBottomSheetDialog" android:id="@+id/songBottomSheetDialog"
android:name="com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog.SongBottomSheetDialog" android:name="com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog.SongBottomSheetDialog"

View file

@ -282,8 +282,8 @@
<string name="settings_delete_download_storage_summary">Wenn Du weitermachst werden alle gespeicherten Inhalte unwiderruflich gelöscht.</string> <string name="settings_delete_download_storage_summary">Wenn Du weitermachst werden alle gespeicherten Inhalte unwiderruflich gelöscht.</string>
<string name="settings_delete_download_storage_title">Gespeicherte Inhalte löschen</string> <string name="settings_delete_download_storage_title">Gespeicherte Inhalte löschen</string>
<string name="settings_download_storage_title">Download storage</string> <string name="settings_download_storage_title">Download storage</string>
<string name="settings_equalizer_summary">Audio Einstellungen anpassen</string> <string name="settings_system_equalizer_summary">Audio Einstellungen anpassen</string>
<string name="settings_equalizer_title">Equalizer</string> <string name="settings_system_equalizer_title">System-Equalizer</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Verfolge die Entwicklung</string> <string name="settings_github_summary">Verfolge die Entwicklung</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -257,8 +257,8 @@
<string name="search_hint">Buscar pista, artistas o álbumes</string> <string name="search_hint">Buscar pista, artistas o álbumes</string>
<string name="search_info_minimum_characters">Introduzca al menos tres caracteres</string> <string name="search_info_minimum_characters">Introduzca al menos tres caracteres</string>
<string name="search_title_album">Álbumes</string> <string name="search_title_album">Álbumes</string>
<string name="settings_equalizer_summary">Ajustes de audio</string> <string name="settings_system_equalizer_summary">Ajustes de audio</string>
<string name="settings_equalizer_title">Ecualizador</string> <string name="settings_system_equalizer_title">Ecualizador del sistema</string>
<string name="search_title_artist">Artistas</string> <string name="search_title_artist">Artistas</string>
<string name="search_title_song">Pistas</string> <string name="search_title_song">Pistas</string>
<string name="server_signup_dialog_action_low_security">Baja seguridad</string> <string name="server_signup_dialog_action_low_security">Baja seguridad</string>
@ -435,4 +435,10 @@
<string name="home_sync_starred_albums_title">Sincronizar álbumes favoritos</string> <string name="home_sync_starred_albums_title">Sincronizar álbumes favoritos</string>
<string name="settings_sync_starred_albums_for_offline_use_summary">Si está habilitada, los álbumes favoritos se descargarán para uso sin conexión.</string> <string name="settings_sync_starred_albums_for_offline_use_summary">Si está habilitada, los álbumes favoritos se descargarán para uso sin conexión.</string>
<string name="starred_album_sync_dialog_summary">Descargar los álbumes favoritos puede consumir una gran cantidad de datos.</string> <string name="starred_album_sync_dialog_summary">Descargar los álbumes favoritos puede consumir una gran cantidad de datos.</string>
<string name="equalizer_fragment_title">Ecualizador</string>
<string name="equalizer_reset">Restablecer</string>
<string name="equalizer_enable">Habilitar</string>
<string name="equalizer_not_supported">No disponible en este dispositivo</string>
<string name="settings_app_equalizer">Ecualizador</string>
<string name="settings_app_equalizer_summary">Abrir el ecualizador incorporado</string>
</resources> </resources>

View file

@ -296,8 +296,8 @@
<string name="settings_delete_download_storage_summary">Continuer entraînera la suppression irréversible de tous les éléments sauvegardés.</string> <string name="settings_delete_download_storage_summary">Continuer entraînera la suppression irréversible de tous les éléments sauvegardés.</string>
<string name="settings_delete_download_storage_title">Supprimer les éléments sauvegardés</string> <string name="settings_delete_download_storage_title">Supprimer les éléments sauvegardés</string>
<string name="settings_download_storage_title">Stockage des téléchargements</string> <string name="settings_download_storage_title">Stockage des téléchargements</string>
<string name="settings_equalizer_summary">Ajuster les paramètres audios</string> <string name="settings_system_equalizer_summary">Ajuster les paramètres audios</string>
<string name="settings_equalizer_title">Égaliseur</string> <string name="settings_system_equalizer_title">Égaliseur du système</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Suivre le développement</string> <string name="settings_github_summary">Suivre le développement</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -282,8 +282,8 @@
<string name="settings_delete_download_storage_summary">Continuando, tutti gli elementi salvati verranno eliminati in modo irreversibile.</string> <string name="settings_delete_download_storage_summary">Continuando, tutti gli elementi salvati verranno eliminati in modo irreversibile.</string>
<string name="settings_delete_download_storage_title">Elimina elementi salvati</string> <string name="settings_delete_download_storage_title">Elimina elementi salvati</string>
<string name="settings_download_storage_title">Archivio download</string> <string name="settings_download_storage_title">Archivio download</string>
<string name="settings_equalizer_summary">Regola le impostazioni audio</string> <string name="settings_system_equalizer_summary">Regola le impostazioni audio</string>
<string name="settings_equalizer_title">Equalizzatore</string> <string name="settings_system_equalizer_title">Equalizzatore di sistema</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Segui lo sviluppo</string> <string name="settings_github_summary">Segui lo sviluppo</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -248,8 +248,8 @@
<string name="settings_delete_download_storage_summary">계속하면 저장된 모든 항목을 완전히 삭제합니다.</string> <string name="settings_delete_download_storage_summary">계속하면 저장된 모든 항목을 완전히 삭제합니다.</string>
<string name="settings_delete_download_storage_title">저장된 항목 삭제</string> <string name="settings_delete_download_storage_title">저장된 항목 삭제</string>
<string name="settings_download_storage_title">스토리지 다운로드</string> <string name="settings_download_storage_title">스토리지 다운로드</string>
<string name="settings_equalizer_summary">오디오 설정 적용</string> <string name="settings_system_equalizer_summary">오디오 설정 적용</string>
<string name="settings_equalizer_title">이퀄라이저</string> <string name="settings_system_equalizer_title">시스템 이퀄라이저</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Follow the development</string> <string name="settings_github_summary">Follow the development</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -289,8 +289,8 @@
<string name="settings_delete_download_storage_summary">Zatwierdzenie nieodwracalnie usunie wszystkie zapisane elementy</string> <string name="settings_delete_download_storage_summary">Zatwierdzenie nieodwracalnie usunie wszystkie zapisane elementy</string>
<string name="settings_delete_download_storage_title">Usuń zapisane elementy</string> <string name="settings_delete_download_storage_title">Usuń zapisane elementy</string>
<string name="settings_download_storage_title">Pamięć do pobierania</string> <string name="settings_download_storage_title">Pamięć do pobierania</string>
<string name="settings_equalizer_summary">Zmień ustawienia audio</string> <string name="settings_system_equalizer_summary">Zmień ustawienia audio</string>
<string name="settings_equalizer_title">Equalizer</string> <string name="settings_system_equalizer_title">Korektor systemowy</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Śledź tworzenie aplikacji</string> <string name="settings_github_summary">Śledź tworzenie aplikacji</string>
<string name="settings_github_title">GitHub</string> <string name="settings_github_title">GitHub</string>

View file

@ -248,8 +248,8 @@
<string name="settings_delete_download_storage_summary">O processo resultará na exclusão irreversível de todos os itens salvos.</string> <string name="settings_delete_download_storage_summary">O processo resultará na exclusão irreversível de todos os itens salvos.</string>
<string name="settings_delete_download_storage_title">Excluir itens salvos</string> <string name="settings_delete_download_storage_title">Excluir itens salvos</string>
<string name="settings_download_storage_title">Armazenamento dos downloads</string> <string name="settings_download_storage_title">Armazenamento dos downloads</string>
<string name="settings_equalizer_summary">Ajustar configurações de áudio</string> <string name="settings_system_equalizer_summary">Ajustar configurações de áudio</string>
<string name="settings_equalizer_title">Equalizador</string> <string name="settings_system_equalizer_title">Equalizador do sistema</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Acompanhe o desenvolvimento</string> <string name="settings_github_summary">Acompanhe o desenvolvimento</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -294,8 +294,8 @@
<string name="settings_delete_download_storage_summary">Продолжение приведет к необратимому удалению всех сохраненных элементов.</string> <string name="settings_delete_download_storage_summary">Продолжение приведет к необратимому удалению всех сохраненных элементов.</string>
<string name="settings_delete_download_storage_title">Удалить сохраненные элементы</string> <string name="settings_delete_download_storage_title">Удалить сохраненные элементы</string>
<string name="settings_download_storage_title">Загрузить хранилище</string> <string name="settings_download_storage_title">Загрузить хранилище</string>
<string name="settings_equalizer_summary">Отрегулируйте настройки звука</string> <string name="settings_system_equalizer_summary">Отрегулируйте настройки звука</string>
<string name="settings_equalizer_title">Эквалайзер</string> <string name="settings_system_equalizer_title">Системный эквалайзер</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Следите за развитием</string> <string name="settings_github_summary">Следите за развитием</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -293,8 +293,8 @@
<string name="settings_delete_download_storage_summary">Devam ederseniz tüm kayıtlı öğeler geri alınamaz şekilde silinecektir.</string> <string name="settings_delete_download_storage_summary">Devam ederseniz tüm kayıtlı öğeler geri alınamaz şekilde silinecektir.</string>
<string name="settings_delete_download_storage_title">Kayıtlı öğeleri sil</string> <string name="settings_delete_download_storage_title">Kayıtlı öğeleri sil</string>
<string name="settings_download_storage_title">İndirme depolaması</string> <string name="settings_download_storage_title">İndirme depolaması</string>
<string name="settings_equalizer_summary">Ses ayarlarını düzenle</string> <string name="settings_system_equalizer_summary">Ses ayarlarını düzenle</string>
<string name="settings_equalizer_title">Ekolayzır</string> <string name="settings_system_equalizer_title">Sistem ekolayzır</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Gelişmeleri takip et</string> <string name="settings_github_summary">Gelişmeleri takip et</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -255,8 +255,8 @@
<string name="settings_delete_download_storage_summary">继续当前操作将导致所有已保存的项目被永久删除。</string> <string name="settings_delete_download_storage_summary">继续当前操作将导致所有已保存的项目被永久删除。</string>
<string name="settings_delete_download_storage_title">删除已保存的项目</string> <string name="settings_delete_download_storage_title">删除已保存的项目</string>
<string name="settings_download_storage_title">下载存储</string> <string name="settings_download_storage_title">下载存储</string>
<string name="settings_equalizer_summary">调整音频设置</string> <string name="settings_system_equalizer_summary">调整音频设置</string>
<string name="settings_equalizer_title">均衡器</string> <string name="settings_system_equalizer_title">系统均衡器</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">关注开发进展</string> <string name="settings_github_summary">关注开发进展</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>

View file

@ -298,8 +298,8 @@
<string name="settings_delete_download_storage_summary">Proceeding will result in the irreversible deletion of all saved items.</string> <string name="settings_delete_download_storage_summary">Proceeding will result in the irreversible deletion of all saved items.</string>
<string name="settings_delete_download_storage_title">Delete saved items</string> <string name="settings_delete_download_storage_title">Delete saved items</string>
<string name="settings_download_storage_title">Download storage</string> <string name="settings_download_storage_title">Download storage</string>
<string name="settings_equalizer_summary">Adjust audio settings</string> <string name="settings_system_equalizer_summary">Adjust audio settings</string>
<string name="settings_equalizer_title">Equalizer</string> <string name="settings_system_equalizer_title">System equalizer</string>
<string name="settings_github_link">https://github.com/eddyizm/tempo</string> <string name="settings_github_link">https://github.com/eddyizm/tempo</string>
<string name="settings_github_summary">Follow the development</string> <string name="settings_github_summary">Follow the development</string>
<string name="settings_github_title">Github</string> <string name="settings_github_title">Github</string>
@ -443,4 +443,10 @@
<item quantity="one">%d album to sync</item> <item quantity="one">%d album to sync</item>
<item quantity="other">%d albums to sync</item> <item quantity="other">%d albums to sync</item>
</plurals> </plurals>
<string name="equalizer_fragment_title">Equalizer</string>
<string name="equalizer_reset">Reset</string>
<string name="equalizer_enable">Enable</string>
<string name="equalizer_not_supported">Not supported on this device</string>
<string name="settings_app_equalizer">Equalizer</string>
<string name="settings_app_equalizer_summary">Open the built-in equalizer</string>
</resources> </resources>

View file

@ -2,9 +2,14 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory app:title="@string/settings_title_general"> <PreferenceCategory app:title="@string/settings_title_general">
<Preference <Preference
android:key="equalizer" android:key="system_equalizer"
android:title="@string/settings_equalizer_title" android:title="@string/settings_system_equalizer_title"
android:summary="@string/settings_equalizer_summary" /> android:summary="@string/settings_system_equalizer_summary" />
<Preference
android:key="app_equalizer"
android:title="@string/settings_app_equalizer"
android:summary="@string/settings_app_equalizer_summary" />
<Preference <Preference
android:key="scan_library" android:key="scan_library"

View file

@ -5,7 +5,9 @@ import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.TaskStackBuilder import android.app.TaskStackBuilder
import android.content.Intent import android.content.Intent
import android.os.Binder
import android.os.Bundle import android.os.Bundle
import android.os.IBinder
import androidx.media3.common.* import androidx.media3.common.*
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.DefaultLoadControl
@ -34,9 +36,18 @@ class MediaService : MediaLibraryService() {
private lateinit var mediaLibrarySession: MediaLibrarySession private lateinit var mediaLibrarySession: MediaLibrarySession
private lateinit var shuffleCommands: List<CommandButton> private lateinit var shuffleCommands: List<CommandButton>
private lateinit var repeatCommands: List<CommandButton> private lateinit var repeatCommands: List<CommandButton>
lateinit var equalizerManager: EqualizerManager
private var customLayout = ImmutableList.of<CommandButton>() private var customLayout = ImmutableList.of<CommandButton>()
inner class LocalBinder : Binder() {
fun getEqualizerManager(): EqualizerManager {
return this@MediaService.equalizerManager
}
}
private val binder = LocalBinder()
companion object { companion object {
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON = private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON =
"android.media3.session.demo.SHUFFLE_ON" "android.media3.session.demo.SHUFFLE_ON"
@ -48,6 +59,7 @@ class MediaService : MediaLibraryService() {
"android.media3.session.demo.REPEAT_ONE" "android.media3.session.demo.REPEAT_ONE"
private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL = private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL =
"android.media3.session.demo.REPEAT_ALL" "android.media3.session.demo.REPEAT_ALL"
const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
} }
override fun onCreate() { override fun onCreate() {
@ -57,6 +69,7 @@ class MediaService : MediaLibraryService() {
initializePlayer() initializePlayer()
initializeMediaLibrarySession() initializeMediaLibrarySession()
initializePlayerListener() initializePlayerListener()
initializeEqualizerManager()
setPlayer(player) setPlayer(player)
} }
@ -66,10 +79,20 @@ class MediaService : MediaLibraryService() {
} }
override fun onDestroy() { override fun onDestroy() {
equalizerManager.release()
releasePlayer() releasePlayer()
super.onDestroy() super.onDestroy()
} }
override fun onBind(intent: Intent?): IBinder? {
// Check if the intent is for our custom equalizer binder
if (intent?.action == ACTION_BIND_EQUALIZER) {
return binder
}
// Otherwise, handle it as a normal MediaLibraryService connection
return super.onBind(intent)
}
private inner class CustomMediaLibrarySessionCallback : MediaLibrarySession.Callback { private inner class CustomMediaLibrarySessionCallback : MediaLibrarySession.Callback {
override fun onConnect( override fun onConnect(
@ -197,6 +220,23 @@ class MediaService : MediaLibraryService() {
player.repeatMode = Preferences.getRepeatMode() player.repeatMode = Preferences.getRepeatMode()
} }
private fun initializeEqualizerManager() {
equalizerManager = EqualizerManager()
val audioSessionId = player.audioSessionId
if (equalizerManager.attachToSession(audioSessionId)) {
val enabled = Preferences.isEqualizerEnabled()
equalizerManager.setEnabled(enabled)
val bands = equalizerManager.getNumberOfBands()
val savedLevels = Preferences.getEqualizerBandLevels(bands)
if (savedLevels != null) {
for (i in 0 until bands) {
equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
}
}
}
}
private fun initializeMediaLibrarySession() { private fun initializeMediaLibrarySession() {
val sessionActivityPendingIntent = val sessionActivityPendingIntent =
TaskStackBuilder.create(this).run { TaskStackBuilder.create(this).run {

View file

@ -4,6 +4,8 @@ import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.TaskStackBuilder import android.app.TaskStackBuilder
import android.content.Intent import android.content.Intent
import android.os.Binder
import android.os.IBinder
import androidx.media3.cast.CastPlayer import androidx.media3.cast.CastPlayer
import androidx.media3.cast.SessionAvailabilityListener import androidx.media3.cast.SessionAvailabilityListener
import androidx.media3.common.AudioAttributes import androidx.media3.common.AudioAttributes
@ -34,6 +36,19 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
private lateinit var castPlayer: CastPlayer private lateinit var castPlayer: CastPlayer
private lateinit var mediaLibrarySession: MediaLibrarySession private lateinit var mediaLibrarySession: MediaLibrarySession
private lateinit var librarySessionCallback: MediaLibrarySessionCallback private lateinit var librarySessionCallback: MediaLibrarySessionCallback
lateinit var equalizerManager: EqualizerManager
inner class LocalBinder : Binder() {
fun getEqualizerManager(): EqualizerManager {
return this@MediaService.equalizerManager
}
}
private val binder = LocalBinder()
companion object {
const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -43,6 +58,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
initializeCastPlayer() initializeCastPlayer()
initializeMediaLibrarySession() initializeMediaLibrarySession()
initializePlayerListener() initializePlayerListener()
initializeEqualizerManager()
setPlayer( setPlayer(
null, null,
@ -63,10 +79,20 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
} }
override fun onDestroy() { override fun onDestroy() {
equalizerManager.release()
releasePlayer() releasePlayer()
super.onDestroy() super.onDestroy()
} }
override fun onBind(intent: Intent?): IBinder? {
// Check if the intent is for our custom equalizer binder
if (intent?.action == ACTION_BIND_EQUALIZER) {
return binder
}
// Otherwise, handle it as a normal MediaLibraryService connection
return super.onBind(intent)
}
private fun initializeRepository() { private fun initializeRepository() {
automotiveRepository = AutomotiveRepository() automotiveRepository = AutomotiveRepository()
} }
@ -85,6 +111,23 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
player.repeatMode = Preferences.getRepeatMode() player.repeatMode = Preferences.getRepeatMode()
} }
private fun initializeEqualizerManager() {
equalizerManager = EqualizerManager()
val audioSessionId = player.audioSessionId
if (equalizerManager.attachToSession(audioSessionId)) {
val enabled = Preferences.isEqualizerEnabled()
equalizerManager.setEnabled(enabled)
val bands = equalizerManager.getNumberOfBands()
val savedLevels = Preferences.getEqualizerBandLevels(bands)
if (savedLevels != null) {
for (i in 0 until bands) {
equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
}
}
}
}
private fun initializeCastPlayer() { private fun initializeCastPlayer() {
if (GoogleApiAvailability.getInstance() if (GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS .isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS

View file

@ -4,6 +4,8 @@ import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.TaskStackBuilder import android.app.TaskStackBuilder
import android.content.Intent import android.content.Intent
import android.os.Binder
import android.os.IBinder
import androidx.media3.cast.CastPlayer import androidx.media3.cast.CastPlayer
import androidx.media3.cast.SessionAvailabilityListener import androidx.media3.cast.SessionAvailabilityListener
import androidx.media3.common.AudioAttributes import androidx.media3.common.AudioAttributes
@ -34,6 +36,19 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
private lateinit var castPlayer: CastPlayer private lateinit var castPlayer: CastPlayer
private lateinit var mediaLibrarySession: MediaLibrarySession private lateinit var mediaLibrarySession: MediaLibrarySession
private lateinit var librarySessionCallback: MediaLibrarySessionCallback private lateinit var librarySessionCallback: MediaLibrarySessionCallback
lateinit var equalizerManager: EqualizerManager
inner class LocalBinder : Binder() {
fun getEqualizerManager(): EqualizerManager {
return this@MediaService.equalizerManager
}
}
private val binder = LocalBinder()
companion object {
const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -43,6 +58,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
initializeCastPlayer() initializeCastPlayer()
initializeMediaLibrarySession() initializeMediaLibrarySession()
initializePlayerListener() initializePlayerListener()
initializeEqualizerManager()
setPlayer( setPlayer(
null, null,
@ -63,14 +79,41 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
} }
override fun onDestroy() { override fun onDestroy() {
equalizerManager.release()
releasePlayer() releasePlayer()
super.onDestroy() super.onDestroy()
} }
override fun onBind(intent: Intent?): IBinder? {
// Check if the intent is for our custom equalizer binder
if (intent?.action == ACTION_BIND_EQUALIZER) {
return binder
}
// Otherwise, handle it as a normal MediaLibraryService connection
return super.onBind(intent)
}
private fun initializeRepository() { private fun initializeRepository() {
automotiveRepository = AutomotiveRepository() automotiveRepository = AutomotiveRepository()
} }
private fun initializeEqualizerManager() {
equalizerManager = EqualizerManager()
val audioSessionId = player.audioSessionId
if (equalizerManager.attachToSession(audioSessionId)) {
val enabled = Preferences.isEqualizerEnabled()
equalizerManager.setEnabled(enabled)
val bands = equalizerManager.getNumberOfBands()
val savedLevels = Preferences.getEqualizerBandLevels(bands)
if (savedLevels != null) {
for (i in 0 until bands) {
equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
}
}
}
}
private fun initializePlayer() { private fun initializePlayer() {
player = ExoPlayer.Builder(this) player = ExoPlayer.Builder(this)
.setRenderersFactory(getRenderersFactory()) .setRenderersFactory(getRenderersFactory())