mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
feat: Built-in audio equalizer (#94)
This commit is contained in:
commit
3f5749f7e1
26 changed files with 891 additions and 31 deletions
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,237 @@
|
||||||
|
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? {
|
||||||
|
val root = inflater.inflate(R.layout.fragment_equalizer, container, false)
|
||||||
|
eqSwitch = root.findViewById(R.id.equalizer_switch)
|
||||||
|
eqSwitch.isChecked = Preferences.isEqualizerEnabled()
|
||||||
|
eqSwitch.jumpDrawablesToCurrentState()
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
eqBandsContainer = view.findViewById(R.id.eq_bands_container)
|
||||||
|
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)
|
||||||
|
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 formatDb(value: Int): String = if (value > 0) "+$value dB" else "$value dB"
|
||||||
|
|
||||||
|
private fun createBandSliders() {
|
||||||
|
val manager = equalizerManager ?: return
|
||||||
|
eqBandsContainer.removeAllViews()
|
||||||
|
bandSeekBars.clear()
|
||||||
|
val bands = manager.getNumberOfBands()
|
||||||
|
val bandLevelRange = manager.getBandLevelRange() ?: shortArrayOf(-1500, 1500)
|
||||||
|
val minLevelDb = bandLevelRange[0] / 100
|
||||||
|
val maxLevelDb = bandLevelRange[1] / 100
|
||||||
|
|
||||||
|
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 {
|
||||||
|
val topBottomMarginDp = 16
|
||||||
|
topMargin = topBottomMarginDp.dpToPx(context)
|
||||||
|
bottomMargin = topBottomMarginDp.dpToPx(context)
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
gravity = Gravity.START
|
||||||
|
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f)
|
||||||
|
}
|
||||||
|
row.addView(freqLabel)
|
||||||
|
|
||||||
|
val initialLevelDb = (savedLevels.getOrNull(i) ?: (manager.getBandLevel(band) ?: 0)) / 100
|
||||||
|
val dbLabel = TextView(requireContext(), null, 0, R.style.LabelSmall).apply {
|
||||||
|
text = formatDb(initialLevelDb)
|
||||||
|
setPadding(12, 0, 0, 0)
|
||||||
|
gravity = Gravity.END
|
||||||
|
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f)
|
||||||
|
}
|
||||||
|
|
||||||
|
val seekBar = SeekBar(requireContext()).apply {
|
||||||
|
max = maxLevelDb - minLevelDb
|
||||||
|
progress = initialLevelDb - minLevelDb
|
||||||
|
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 6f)
|
||||||
|
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||||
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||||
|
val thisLevelDb = progress + minLevelDb
|
||||||
|
if (fromUser) {
|
||||||
|
manager.setBandLevel(band, (thisLevelDb * 100).toShort())
|
||||||
|
saveBandLevelsToPreferences()
|
||||||
|
}
|
||||||
|
dbLabel.text = formatDb(thisLevelDb)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 minLevelDb = bandLevelRange[0] / 100
|
||||||
|
val midLevelDb = 0
|
||||||
|
|
||||||
|
for (i in 0 until bands) {
|
||||||
|
manager.setBandLevel(i.toShort(), (0).toShort())
|
||||||
|
bandSeekBars.getOrNull(i)?.progress = midLevelDb - minLevelDb
|
||||||
|
}
|
||||||
|
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 minLevelDb = bandLevelRange[0] / 100
|
||||||
|
|
||||||
|
val savedLevels = Preferences.getEqualizerBandLevels(bands)
|
||||||
|
for (i in 0 until bands) {
|
||||||
|
val savedDb = savedLevels[i] / 100
|
||||||
|
manager.setBandLevel(i.toShort(), (savedDb * 100).toShort())
|
||||||
|
bandSeekBars.getOrNull(i)?.progress = savedDb - minLevelDb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Int.dpToPx(context: Context): Int =
|
||||||
|
(this * context.resources.displayMetrics.density).toInt()
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -24,11 +28,14 @@ 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;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerControllerBinding;
|
import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerControllerBinding;
|
||||||
|
import com.cappielloantonio.tempo.service.EqualizerManager;
|
||||||
import com.cappielloantonio.tempo.service.MediaService;
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.RatingDialog;
|
import com.cappielloantonio.tempo.ui.dialog.RatingDialog;
|
||||||
|
|
@ -68,11 +75,15 @@ 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;
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
|
||||||
|
private MediaService.LocalBinder mediaServiceBinder;
|
||||||
|
private boolean isServiceBound = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
activity = (MainActivity) getActivity();
|
activity = (MainActivity) getActivity();
|
||||||
|
|
@ -89,6 +100,7 @@ public class PlayerControllerFragment extends Fragment {
|
||||||
initMediaListenable();
|
initMediaListenable();
|
||||||
initMediaLabelButton();
|
initMediaLabelButton();
|
||||||
initArtistLabelButton();
|
initArtistLabelButton();
|
||||||
|
initEqualizerButton();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
@ -126,6 +138,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 +439,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);
|
||||||
}
|
}
|
||||||
|
|
@ -461,4 +486,66 @@ public class PlayerControllerFragment extends Fragment {
|
||||||
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_100));
|
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_100));
|
||||||
// TODO Resettare lo skip del silenzio
|
// TODO Resettare lo skip del silenzio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
mediaServiceBinder = (MediaService.LocalBinder) service;
|
||||||
|
isServiceBound = true;
|
||||||
|
checkEqualizerBands();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
mediaServiceBinder = null;
|
||||||
|
isServiceBound = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void bindMediaService() {
|
||||||
|
Intent intent = new Intent(requireActivity(), MediaService.class);
|
||||||
|
intent.setAction(MediaService.ACTION_BIND_EQUALIZER);
|
||||||
|
requireActivity().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||||
|
isServiceBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkEqualizerBands() {
|
||||||
|
if (mediaServiceBinder != null) {
|
||||||
|
EqualizerManager eqManager = mediaServiceBinder.getEqualizerManager();
|
||||||
|
short numBands = eqManager.getNumberOfBands();
|
||||||
|
|
||||||
|
if (equalizerButton != null) {
|
||||||
|
if (numBands == 0) {
|
||||||
|
equalizerButton.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) playerOpenQueueButton.getLayoutParams();
|
||||||
|
params.startToEnd = ConstraintLayout.LayoutParams.UNSET;
|
||||||
|
params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
|
||||||
|
playerOpenQueueButton.setLayoutParams(params);
|
||||||
|
} else {
|
||||||
|
equalizerButton.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) playerOpenQueueButton.getLayoutParams();
|
||||||
|
params.startToStart = ConstraintLayout.LayoutParams.UNSET;
|
||||||
|
params.startToEnd = R.id.player_open_equalizer_button;
|
||||||
|
playerOpenQueueButton.setLayoutParams(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
bindMediaService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (isServiceBound) {
|
||||||
|
requireActivity().unbindService(serviceConnection);
|
||||||
|
isServiceBound = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.media.audiofx.AudioEffect;
|
import android.media.audiofx.AudioEffect;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
@ -18,6 +22,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;
|
||||||
|
|
@ -28,6 +35,8 @@ import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
||||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||||
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
||||||
|
import com.cappielloantonio.tempo.service.EqualizerManager;
|
||||||
|
import com.cappielloantonio.tempo.service.MediaService;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
||||||
|
|
@ -51,6 +60,9 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
private ActivityResultLauncher<Intent> someActivityResultLauncher;
|
private ActivityResultLauncher<Intent> someActivityResultLauncher;
|
||||||
|
|
||||||
|
private MediaService.LocalBinder mediaServiceBinder;
|
||||||
|
private boolean isServiceBound = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
@ -86,7 +98,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
checkEqualizer();
|
checkSystemEqualizer();
|
||||||
checkCacheStorage();
|
checkCacheStorage();
|
||||||
checkStorage();
|
checkStorage();
|
||||||
|
|
||||||
|
|
@ -102,6 +114,9 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
actionChangeDownloadStorage();
|
actionChangeDownloadStorage();
|
||||||
actionDeleteDownloadStorage();
|
actionDeleteDownloadStorage();
|
||||||
actionKeepScreenOn();
|
actionKeepScreenOn();
|
||||||
|
|
||||||
|
bindMediaService();
|
||||||
|
actionAppEqualizer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -124,8 +139,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 +368,63 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
mediaServiceBinder = (MediaService.LocalBinder) service;
|
||||||
|
isServiceBound = true;
|
||||||
|
checkEqualizerBands();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
mediaServiceBinder = null;
|
||||||
|
isServiceBound = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void bindMediaService() {
|
||||||
|
Intent intent = new Intent(requireActivity(), MediaService.class);
|
||||||
|
intent.setAction(MediaService.ACTION_BIND_EQUALIZER);
|
||||||
|
requireActivity().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||||
|
isServiceBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkEqualizerBands() {
|
||||||
|
if (mediaServiceBinder != null) {
|
||||||
|
EqualizerManager eqManager = mediaServiceBinder.getEqualizerManager();
|
||||||
|
short numBands = eqManager.getNumberOfBands();
|
||||||
|
Preference appEqualizer = findPreference("app_equalizer");
|
||||||
|
if (appEqualizer != null) {
|
||||||
|
appEqualizer.setVisible(numBands > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (isServiceBound) {
|
||||||
|
requireActivity().unbindService(serviceConnection);
|
||||||
|
isServiceBound = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
11
app/src/main/res/drawable/ic_eq.xml
Normal file
11
app/src/main/res/drawable/ic_eq.xml
Normal 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>
|
||||||
93
app/src/main/res/drawable/ui_eq_not_supported.xml
Normal file
93
app/src/main/res/drawable/ui_eq_not_supported.xml
Normal 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>
|
||||||
|
|
@ -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>
|
||||||
105
app/src/main/res/layout/fragment_equalizer.xml
Normal file
105
app/src/main/res/layout/fragment_equalizer.xml
Normal 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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 integrado</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -261,8 +261,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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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,21 @@ 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)
|
||||||
|
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 {
|
||||||
|
|
|
||||||
|
|
@ -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,21 @@ 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)
|
||||||
|
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
|
||||||
|
|
|
||||||
|
|
@ -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,39 @@ 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)
|
||||||
|
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())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue