diff --git a/CHANGELOG.md b/CHANGELOG.md index c42c6bff..1b84b06a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ ***This log is for this fork to detail updates since 3.9.0 from the main repo.*** +## [3.13.0](https://github.com/eddyizm/tempo/releases/tag/v3.13.0) (2025-08-23) +## What's Changed +* style: Change position and size of rating container by @jaime-grj in https://github.com/eddyizm/tempo/pull/44 +* feat: Add Turkish localization (values-tr) by @mucahit-kaya in https://github.com/eddyizm/tempo/pull/50 +* chore: adding a note/not fully baked label to the sync user play queue setting by @eddyizm in https://github.com/eddyizm/tempo/commit/8ed0a4642bd0cd637c65e3115142596331fa7ef7 +* fix: moved hardcoded italian save text to string template, updated with english and italian language xmls by @eddyizm in https://github.com/eddyizm/tempo/commit/26a5fb029a07752c9c0db0d08a89afd638772579 + + +## New Contributors +* @mucahit-kaya made their first contribution in https://github.com/eddyizm/tempo/pull/50 + +**Full Changelog**: https://github.com/eddyizm/tempo/compare/v3.12.0...v3.13.0 + +## [3.12.0](https://github.com/eddyizm/tempo/releases/tag/v3.12.0) (2025-08-15) +### What's Changed +* [chore]: add German translations for track info and home section strings (#29) by @BreadWare92 in https://github.com/eddyizm/tempo/pull/31 +* [chore]: increased "Offline mode" text size, changed its color in dark theme by @jaime-grj in https://github.com/eddyizm/tempo/pull/33 +* [chore]: Translations for sections by @skajmer in https://github.com/eddyizm/tempo/pull/30 +* [chore]: Update French localization by @benoit-smith in https://github.com/eddyizm/tempo/pull/36 +* [fix]: Show placeholder string in TrackInfoDialog fields when there is no data by @jaime-grj in https://github.com/eddyizm/tempo/pull/37 +* [feat]: added transcoding codec and bitrate info to PlayerControllerFragment, replace hardcoded strings by @jaime-grj in https://github.com/eddyizm/tempo/pull/38 +* [chore]: Update French localization by @benoit-smith in https://github.com/eddyizm/tempo/pull/39 +* [feat]: show rating on song view by @eddyizm in https://github.com/eddyizm/tempo/pull/40 + +### New Contributors +* @BreadWare92 made their first contribution in https://github.com/eddyizm/tempo/pull/31 +* @skajmer made their first contribution in https://github.com/eddyizm/tempo/pull/30 +* @benoit-smith made their first contribution in https://github.com/eddyizm/tempo/pull/36 + +**Full Changelog**: https://github.com/eddyizm/tempo/compare/v3.11.2...v3.12.0 + ## [3.11.2](https://github.com/eddyizm/tempo/releases/tag/v3.11.2) (2025-08-09) diff --git a/README.md b/README.md index e633e7cc..00680d8d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ This fork is my attempt to keep development moving forward and merge in PR's tha Moved details to [CHANGELOG.md](https://github.com/eddyizm/tempo/blob/main/CHANGELOG.md) +Fork [**sponsorship here**](https://ko-fi.com/eddyizm). + ## Features - **Subsonic Integration**: Tempo seamlessly integrates with your Subsonic server, providing you with easy access to your entire music collection on the go. - **Sleek and Intuitive UI**: Enjoy a clean and user-friendly interface designed to enhance your music listening experience, tailored to your preferences and listening history. @@ -42,9 +44,10 @@ Moved details to [CHANGELOG.md](https://github.com/eddyizm/tempo/blob/main/CHANG - **Android Auto Support**: Enjoy your favorite music on the go with full Android Auto integration, allowing you to seamlessly control and listen to your tracks directly from your mobile device while driving. ## Sponsors +Thanks to the original repo/creator [CappielloAntonio](https://github.com/CappielloAntonio) (3.9.0) + Tempo is an open-source project developed and maintained solely by me. I would like to express my heartfelt thanks to all the users who have shown their love and support for Tempo. Your contributions and encouragement mean a lot to me, and they help drive the development and improvement of the app. -If you would like to sponsor the project and show your support, you can make a donation or contribution by visiting the [**sponsorship page**](https://www.buymeacoffee.com/a.cappiello). Your generosity will help cover the costs of development and further enhancements. ## Screenshot diff --git a/app/build.gradle b/app/build.gradle index ea649294..5926fa3f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { minSdkVersion 24 targetSdk 35 - versionCode 27 - versionName '3.11.2' + versionCode 29 + versionName '3.13.0' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java index 5c0b119c..360a5ec5 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/PlaylistChooserDialog.java @@ -3,6 +3,7 @@ package com.cappielloantonio.tempo.ui.dialog; import android.app.Dialog; import android.os.Bundle; import android.view.View; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; @@ -97,8 +98,12 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba @Override public void onPlaylistClick(Bundle bundle) { - Playlist playlist = bundle.getParcelable(Constants.PLAYLIST_OBJECT); - playlistChooserViewModel.addSongsToPlaylist(playlist.getId()); - dismiss(); + if (playlistChooserViewModel.getSongsToAdd() != null && !playlistChooserViewModel.getSongsToAdd().isEmpty()) { + Playlist playlist = bundle.getParcelable(Constants.PLAYLIST_OBJECT); + playlistChooserViewModel.addSongsToPlaylist(playlist.getId()); + dismiss(); + } else { + Toast.makeText(requireContext(), R.string.playlist_chooser_dialog_toast_add_failure, Toast.LENGTH_SHORT).show(); + } } } diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/RatingDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/RatingDialog.java index fc829152..a2d16003 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/RatingDialog.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/RatingDialog.java @@ -63,7 +63,11 @@ public class RatingDialog extends DialogFragment { bind.ratingBar.setRating(song.getUserRating() != null ? song.getUserRating() : 0); }); } else if (ratingViewModel.getAlbum() != null) { - ratingViewModel.getLiveAlbum().observe(this, album -> bind.ratingBar.setRating(/*album.getRating()*/ 0)); + ratingViewModel.getLiveAlbum().observe(this, album -> { + if (album != null) { + bind.ratingBar.setRating(album.getUserRating() != null ? album.getUserRating() : 0); + } + }); } else if (ratingViewModel.getArtist() != null) { ratingViewModel.getLiveArtist().observe(this, artist -> bind.ratingBar.setRating(/*artist.getRating()*/ 0)); } diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumListPageFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumListPageFragment.java index eb5b19c5..9a1557a6 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumListPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumListPageFragment.java @@ -145,7 +145,7 @@ public class AlbumListPageFragment extends Fragment implements ClickCallback { @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - inflater.inflate(R.menu.toolbar_menu, menu); + inflater.inflate(R.menu.artist_list_menu, menu); MenuItem searchItem = menu.findItem(R.id.action_search); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumPageFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumPageFragment.java index dc02dcbe..03e71e10 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumPageFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/AlbumPageFragment.java @@ -4,6 +4,7 @@ import android.content.ComponentName; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.Parcelable; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -27,11 +28,13 @@ import com.cappielloantonio.tempo.databinding.FragmentAlbumPageBinding; import com.cappielloantonio.tempo.glide.CustomGlideRequest; import com.cappielloantonio.tempo.interfaces.ClickCallback; import com.cappielloantonio.tempo.model.Download; +import com.cappielloantonio.tempo.subsonic.models.AlbumID3; import com.cappielloantonio.tempo.service.MediaManager; import com.cappielloantonio.tempo.service.MediaService; import com.cappielloantonio.tempo.ui.activity.MainActivity; import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter; import com.cappielloantonio.tempo.ui.dialog.PlaylistChooserDialog; +import com.cappielloantonio.tempo.ui.dialog.RatingDialog; import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.MappingUtil; @@ -104,6 +107,16 @@ public class AlbumPageFragment extends Fragment implements ClickCallback { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { + if (item.getItemId() == R.id.action_rate_album) { + Bundle bundle = new Bundle(); + AlbumID3 album = albumPageViewModel.getAlbum().getValue(); + bundle.putParcelable(Constants.ALBUM_OBJECT, (Parcelable) album); + RatingDialog dialog = new RatingDialog(); + dialog.setArguments(bundle); + dialog.show(requireActivity().getSupportFragmentManager(), null); + return true; + } + if (item.getItemId() == R.id.action_download_album) { albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> { DownloadUtil.getDownloadTracker(requireContext()).download(MappingUtil.mapDownloads(songs), songs.stream().map(Download::new).collect(Collectors.toList())); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/ArtistCatalogueFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/ArtistCatalogueFragment.java index 1ad88e8e..17d01f95 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/ArtistCatalogueFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/ArtistCatalogueFragment.java @@ -13,6 +13,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.PopupMenu; import android.widget.SearchView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -24,6 +25,8 @@ import androidx.navigation.Navigation; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import android.util.Log; + import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.databinding.FragmentArtistCatalogueBinding; import com.cappielloantonio.tempo.helper.recyclerview.GridItemDecoration; @@ -32,6 +35,10 @@ import com.cappielloantonio.tempo.ui.activity.MainActivity; import com.cappielloantonio.tempo.ui.adapter.ArtistCatalogueAdapter; import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.viewmodel.ArtistCatalogueViewModel; +import com.cappielloantonio.tempo.subsonic.models.ArtistID3; + +import java.util.ArrayList; +import java.util.List; @UnstableApi public class ArtistCatalogueFragment extends Fragment implements ClickCallback { @@ -125,23 +132,50 @@ public class ArtistCatalogueFragment extends Fragment implements ClickCallback { SearchView searchView = (SearchView) searchItem.getActionView(); searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); + + searchView.setQueryHint(getString(R.string.filter_artist)); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { - searchView.clearFocus(); - return false; + // this toast may be overkill... + Toast.makeText(requireContext(), "Search: " + query, Toast.LENGTH_SHORT).show(); + filterArtists(query); + return true; } @Override public boolean onQueryTextChange(String newText) { - artistAdapter.getFilter().filter(newText); - return false; + filterArtists(newText); + return true; } }); searchView.setPadding(-32, 0, 0, 0); } + private void filterArtists(String query) { + List allArtists = artistCatalogueViewModel.getArtistList().getValue(); + + if (allArtists == null || allArtists.isEmpty()) { + return; + } + + if (query == null || query.trim().isEmpty()) { + artistAdapter.setItems(allArtists); + } else { + String searchQuery = query.toLowerCase().trim(); + List filteredArtists = new ArrayList<>(); + + for (ArtistID3 artist : allArtists) { + if (artist.getName() != null && + artist.getName().toLowerCase().contains(searchQuery)) { + filteredArtists.add(artist); + } + } + artistAdapter.setItems(filteredArtists); + } + } + private void hideKeyboard(View view) { InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(view.getWindowToken(), 0); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java index 01d8eb7b..99f3c4ca 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java @@ -8,8 +8,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageButton; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.ToggleButton; +import android.widget.RatingBar; import androidx.annotation.NonNull; import androidx.constraintlayout.widget.ConstraintLayout; @@ -36,6 +38,7 @@ import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.MusicUtil; import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel; +import com.cappielloantonio.tempo.viewmodel.RatingViewModel; import com.google.android.material.chip.Chip; import com.google.android.material.elevation.SurfaceColors; import com.google.common.util.concurrent.ListenableFuture; @@ -53,6 +56,8 @@ public class PlayerControllerFragment extends Fragment { private InnerFragmentPlayerControllerBinding bind; private ViewPager2 playerMediaCoverViewPager; private ToggleButton buttonFavorite; + private RatingViewModel ratingViewModel; + private RatingBar songRatingBar; private TextView playerMediaTitleLabel; private TextView playerArtistNameLabel; private Button playbackSpeedButton; @@ -62,6 +67,7 @@ public class PlayerControllerFragment extends Fragment { private ConstraintLayout playerQuickActionView; private ImageButton playerOpenQueueButton; private ImageButton playerTrackInfo; + private LinearLayout ratingContainer; private MainActivity activity; private PlayerBottomSheetViewModel playerBottomSheetViewModel; @@ -75,6 +81,7 @@ public class PlayerControllerFragment extends Fragment { View view = bind.getRoot(); playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class); + ratingViewModel = new ViewModelProvider(requireActivity()).get(RatingViewModel.class); init(); initQuickActionView(); @@ -117,6 +124,9 @@ public class PlayerControllerFragment extends Fragment { playerQuickActionView = bind.getRoot().findViewById(R.id.player_quick_action_view); playerOpenQueueButton = bind.getRoot().findViewById(R.id.player_open_queue_button); playerTrackInfo = bind.getRoot().findViewById(R.id.player_info_track); + songRatingBar = bind.getRoot().findViewById(R.id.song_rating_bar); + ratingContainer = bind.getRoot().findViewById(R.id.rating_container); + checkAndSetRatingContainerVisibility(); } private void initQuickActionView() { @@ -313,6 +323,7 @@ public class PlayerControllerFragment extends Fragment { private void initMediaListenable() { playerBottomSheetViewModel.getLiveMedia().observe(getViewLifecycleOwner(), media -> { if (media != null) { + ratingViewModel.setSong(media); buttonFavorite.setChecked(media.getStarred() != null); buttonFavorite.setOnClickListener(v -> playerBottomSheetViewModel.setFavorite(requireContext(), media)); buttonFavorite.setOnLongClickListener(v -> { @@ -323,9 +334,29 @@ public class PlayerControllerFragment extends Fragment { dialog.setArguments(bundle); dialog.show(requireActivity().getSupportFragmentManager(), null); + return true; }); + Integer currentRating = media.getUserRating(); + + if (currentRating != null) { + songRatingBar.setRating(currentRating); + } else { + songRatingBar.setRating(0); + } + + songRatingBar.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() { + @Override + public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) { + if (fromUser) { + ratingViewModel.rate((int) rating); + media.setUserRating((int) rating); + } + } + }); + + if (getActivity() != null) { playerBottomSheetViewModel.refreshMediaInfo(requireActivity(), media); } @@ -403,6 +434,17 @@ public class PlayerControllerFragment extends Fragment { playerMediaCoverViewPager.setCurrentItem(1, true); } + private void checkAndSetRatingContainerVisibility() { + if (ratingContainer == null) return; + + if (Preferences.showItemStarRating()) { + ratingContainer.setVisibility(View.VISIBLE); + } + else { + ratingContainer.setVisibility(View.GONE); + } + } + private void setPlaybackParameters(MediaBrowser mediaBrowser) { Button playbackSpeedButton = bind.getRoot().findViewById(R.id.player_playback_speed_button); float currentSpeed = Preferences.getPlaybackSpeed(); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerCoverFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerCoverFragment.java index f44ed41e..392c5786 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerCoverFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerCoverFragment.java @@ -9,6 +9,7 @@ import android.transition.TransitionManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import java.util.ArrayList; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; @@ -19,6 +20,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.session.MediaBrowser; import androidx.media3.session.SessionToken; +import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerCoverBinding; import com.cappielloantonio.tempo.glide.CustomGlideRequest; import com.cappielloantonio.tempo.model.Download; @@ -30,6 +32,7 @@ import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.MappingUtil; import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel; +import com.cappielloantonio.tempo.subsonic.models.Child; import com.google.android.material.snackbar.Snackbar; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; @@ -119,8 +122,10 @@ public class PlayerCoverFragment extends Fragment { }); bind.innerButtonTopRight.setOnClickListener(view -> { + ArrayList tracks = new ArrayList<>(); + tracks.add(song); Bundle bundle = new Bundle(); - bundle.putParcelable(Constants.TRACK_OBJECT, song); + bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, tracks); PlaylistChooserDialog dialog = new PlaylistChooserDialog(); dialog.setArguments(bundle); @@ -136,7 +141,7 @@ public class PlayerCoverFragment extends Fragment { bind.innerButtonBottomRight.setOnClickListener(view -> { if (playerBottomSheetViewModel.savePlayQueue()) { - Snackbar.make(requireView(), "Salvato", Snackbar.LENGTH_LONG).show(); + Snackbar.make(requireView(), R.string.player_queue_save_queue_success, Snackbar.LENGTH_LONG).show(); } }); diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt index e7a4c459..c7bfe993 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt @@ -63,6 +63,7 @@ object Preferences { private const val ALWAYS_ON_DISPLAY = "always_on_display" private const val AUDIO_QUALITY_PER_ITEM = "audio_quality_per_item" private const val HOME_SECTOR_LIST = "home_sector_list" + private const val SONG_RATING_PER_ITEM = "song_rating_per_item" private const val RATING_PER_ITEM = "rating_per_item" private const val NEXT_UPDATE_CHECK = "next_update_check" private const val CONTINUOUS_PLAY = "continuous_play" @@ -486,6 +487,11 @@ object Preferences { App.getInstance().preferences.edit().putString(HOME_SECTOR_LIST, Gson().toJson(extension)).apply() } + @JvmStatic + fun showItemStarRating(): Boolean { + return App.getInstance().preferences.getBoolean(SONG_RATING_PER_ITEM, false) + } + @JvmStatic fun showItemRating(): Boolean { return App.getInstance().preferences.getBoolean(RATING_PER_ITEM, false) diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java index 3e712479..bf90fa65 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java @@ -103,7 +103,6 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel { favoriteRepository.starLater(media.getId(), null, null, false); } }); - media.setStarred(null); } @@ -131,7 +130,7 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel { } } - public LiveData getLiveLyrics() { + public LiveData getLiveLyrics() { return lyricsLiveData; } diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java index fdee85c6..2ec6c21f 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlaylistChooserViewModel.java @@ -21,7 +21,7 @@ public class PlaylistChooserViewModel extends AndroidViewModel { private final PlaylistRepository playlistRepository; private final MutableLiveData> playlists = new MutableLiveData<>(null); - private ArrayList toAdd; + private ArrayList toAdd = new ArrayList<>(); public PlaylistChooserViewModel(@NonNull Application application) { super(application); diff --git a/app/src/main/res/layout-land/inner_fragment_player_controller_layout.xml b/app/src/main/res/layout-land/inner_fragment_player_controller_layout.xml index 61112689..7ad0250e 100644 --- a/app/src/main/res/layout-land/inner_fragment_player_controller_layout.xml +++ b/app/src/main/res/layout-land/inner_fragment_player_controller_layout.xml @@ -75,6 +75,39 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/rating_container" /> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/artist_list_menu.xml b/app/src/main/res/menu/artist_list_menu.xml new file mode 100644 index 00000000..7cc7d34d --- /dev/null +++ b/app/src/main/res/menu/artist_list_menu.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a5f3be74..fe9cd76a 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -183,6 +183,7 @@ Anno %1$.2fx Svuota coda di riproduzione + Salvato Priorità server Catalogo playlist Sfoglia le playlist diff --git a/app/src/main/res/values-tr/arrays.xml b/app/src/main/res/values-tr/arrays.xml new file mode 100644 index 00000000..db3d1427 --- /dev/null +++ b/app/src/main/res/values-tr/arrays.xml @@ -0,0 +1,257 @@ + + + Açık + Koyu + Sistem varsayılanı + + + light + dark + default + + + + Yüksek + Orta + Düşük + + + 500 + 250 + 125 + + + + Yüksek + Orta + Düşük + + + -1 + 500 + 300 + + + + Kapalı + 128 MiB + 256 MiB + 512 MiB + 1024 MiB + + + 0 + 128 + 256 + 512 + 1024 + + + + Orjinal + 32 kbps + 48 kbps + 64 kbps + 80 kbps + 96 kbps + 112 kbps + 128 kbps + 160 kbps + 192 kbps + 256 kbps + 320 kbps + + + 0 + 32 + 48 + 64 + 80 + 96 + 112 + 128 + 160 + 192 + 256 + 320 + + + + Orjinal + 32 kbps + 48 kbps + 64 kbps + 80 kbps + 96 kbps + 112 kbps + 128 kbps + 160 kbps + 192 kbps + 256 kbps + 320 kbps + + + 0 + 32 + 48 + 64 + 80 + 96 + 112 + 128 + 160 + 192 + 256 + 320 + + + + Orjinal + 32 kbps + 48 kbps + 64 kbps + 80 kbps + 96 kbps + 112 kbps + 128 kbps + 160 kbps + 192 kbps + 256 kbps + 320 kbps + + + 0 + 32 + 48 + 64 + 80 + 96 + 112 + 128 + 160 + 192 + 256 + 320 + + + + Doğrudan çal + Opus + AAC + Mp3 + Flac + + + raw + opus + aac + mp3 + flac + + + + Doğrudan çal + Opus + AAC + Mp3 + Flac + + + raw + opus + aac + mp3 + flac + + + + Doğrudan indir + Opus + AAC + Mp3 + Flac + + + raw + opus + aac + mp3 + flac + + + + On saniye + Beş saniye + İki saniye + + + 10 + 5 + 2 + + + + Yüksek + Orta + Düşük + + + 18 + 12 + 6 + + + + Kapalı + Parça + Albüm + Otomatik + + + kapalı + parça + albüm + otomatik + + + + Format dönüştürme yapma + Sunucu ayarları + Wi-fi dönüştürme ayarları + Mobil dönüştürme formatı + + + 0 + 1 + 2 + 3 + + + + Minimum + Moderate + Agrasif + Aşırı + + + .1 + 1 + 4 + 8 + + + + En az 0 yıldız + En az 1 yıldız + En az 2 yıldız + En az 3 yıldız + En az 4 yıldız + + + 0 + 1 + 2 + 3 + 4 + + \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml new file mode 100644 index 00000000..fa915fec --- /dev/null +++ b/app/src/main/res/values-tr/strings.xml @@ -0,0 +1,436 @@ + + Sorun yaşarsanız https://dontkillmyapp.com adresini ziyaret edin. Uygulamanın performansını etkileyebilecek güç tasarrufu özelliklerinin nasıl devre dışı bırakılacağına dair ayrıntılı talimatlar sağlar. + Ekran kapalıyken medya oynatma için lütfen pil optimizasyonlarını devre dışı bırakın. + Pil optimizasyonu + Çevrimdışı mod + Çalma listesine ekle + Sıraya ekle + Tümünü indir + Sanatçıya git + Anında karışım + Sıradakini çal + Tümünü kaldır + Paylaş + Karıştır + Albümler + Albümleri görüntüle + Sanatçı getirilirken hata oluştu + İndirilen albümler + En çok çalınan albümler + Yeni çıkanlar + Yakın zamanda eklenen albümler + Yakın zamanda çalınan albümler + Yıldızlı albümler + Albümler + Buna benzer daha fazla + Çal + %1$s tarihinde yayınlandı + %1$s tarihinde yayınlandı, orijinali %2$s + Karıştır + %1$d parça • %2$d dakika + Tempo + Aranıyor… + Anında karışım + Karıştır + Sanatçılar + Sanatçılara göz at + Sanatçının radyosu alınırken hata oluştu + Sanatçının parçaları alınırken hata oluştu + İndirilen sanatçılar + Yıldızlı sanatçılar + Sanatçılar + Radyo + Karıştır + Düzeni değiştir + Buna benzer daha fazla + Albümler + Daha fazla + Biyografi + En Çok Dinlenen Şarkılar + Tümünü gör + Yok say + Bir daha sorma + Devre dışı bırak + İptal + Veri tasarrufunu etkinleştir + Tamam + Wi-Fi dışındaki bağlantılarda Subsonic sunucusuna erişim kısıtlandı. Bu uyarı penceresinin tekrar görünmemesi için, uygulama ayarlarından bağlantı denetimini devre dışı bırakın. + Wi-Fi bağlı değil + Karıştır + İptal + Devam ettir + Lütfen dikkat edin, bu işleme devam etmek tüm sunuculardan indirilen kayıtlı öğelerin kalıcı olarak silinmesine yol açacaktır. + Kayıtlı öğeleri sil + Açıklama yok + Disk %1$s - %2$s + Disk %1$s + İptal + İndir + Bu klasördeki tüm parçalar indirilecektir. Alt klasörlerde bulunan parçalar indirilmeyecektir. + Parçaları indir + Bir şarkı indirdiğinde burada görünecek + Henüz indirme yok! + %1$s • %2$s öğe + %1$s öğe + Tümünü karıştır + Değişikliklerin geçerli olması için uygulamayı yeniden başlatın. + İndirilen dosyaların hedefi bir depolamadan diğerine değiştirildiğinde, önceki depolamada bulunan tüm indirilmiş dosyalar anında silinecektir. + Depolama seçeneğini seç + Harici + Dahili + İndirilenler + Sıraya ekle + Sonra çal + Kaldır + Tümünü kaldır + Karıştır + + Zorunlu + http veya https öneki gerekli + İndirilenler + İki veya daha fazla filtre seçin + Filtre + Türleri filtrele + (%1$d) + (+%1$d) + Tür Kataloğu + Türlere göz at + Bana sonra hatırlat + Destek ol + Hemen indir + Uygulamanın yeni bir sürümü Github’da mevcut. + Güncelleme mevcut + İptal + Sıfırla + Kaydet + Ana sayfayı düzenle + Yapılan değişikliklerin etkili olması için uygulamayı yeniden başlatmanız gerekir. + Müzik + Podcast + Radyo + Favori sanatçılarının en iyi şarkıları + Beğendiğin bir şarkıdan karışım başlat + Yeni radyo ekle + Yeni podcast kanalı ekle + İptal + İndir + Bu parçaların indirilmesi önemli miktarda veri kullanabilir + Eşitlenecek bazı yıldızlı parçalar var gibi görünüyor + En iyiler + Keşfet + Tümünü karıştır + Geçmişe dönüş + İnternet radyo istasyonları + Son çalınanlar + Tümünü gör + Geçen hafta + Geçen ay + Geçen yıl + Senin için + En çok çalınanlar + Tümünü gör + Yeni çıkışlar + En yeni podcastler + Çalma listeleri + Kanallar + Tümünü gör + Radyo istasyonları + Son eklenenler + Tümünü gör + Paylaşımlar + ★ Yıldızlı albümler + Tümünü gör + ★ Yıldızlı sanatçılar + Tümünü gör + ★ Yıldızlı parçalar + Tümünü gör + En iyi şarkıların + Yeniden düzenle + + -- + Albümler + Tümünü gör + Sanatçılar + Tümünü gör + Türler + Tümünü gör + Müzik klasörleri + Çalma listeleri + Tümünü gör + Sunucu eklenmedi + Subsonic sunucuları + Subsonic sunucuları + Yayınla + Ekle + Çalma listesine ekle + Tümünü indir + İndir + Tümü + İndirilenler + Albüm + Sanatçı + Tür + Parça + Yıl + Ana sayfa + Geçen hafta + Geçen ay + Geçen yıl + Kütüphane + Ara + Ayarlar + Sanatçı + Ad + Rastgele + Son eklenenler + Son çalınanlar + En çok çalınanlar + En son yıldızlananlar + En eski yıldızlananlar + Ana ekrana ekle + Ana ekrandan kaldır + Yıl + %1$.2fx + Çalma sırasını temizle + Sunucu önceliği + Bilinmeyen format + Dönüştürme + talep edildi + Çalma Listesi Kataloğu + Çalma listelerine göz at + Henüz çalma listesi oluşturulmadı + İptal + Oluştur + Çalma listesine ekle + Şarkı çalma listesine eklendi + Şarkı çalma listesine eklenemedi + %1$d parça • %2$s + Süre • %1$s + Silmek için uzun basın + Çalma listesi adı + İptal + Sil + Kaydet + Çalma listesini düzenle + Çal + Karıştır + Çalma listesi • %1$d şarkı + Sıraya ekle + Sil + İndir + Kanala git + Sonra çal + Kaldır + Kanallar + Kanallara göz at + RSS URL + Podcast Kanalı + Açıklama + Bölümler + Bölüm mevcut değil + İsteğiniz sunucuya gönderildi + Bölümü gizlemek için tıklayın\nDeğişiklikler yeniden başlatıldığında görülecek + Bir kanal eklediğinizde burada göreceksiniz + Podcast bulunamadı! + %1$s • %2$s + Radyo Anasayfa URL’si + Radyo Adı + Radyo Yayın URL’si + İptal + Sil + Kaydet + İnternet Radyo İstasyonu + Bölümü gizlemek için tıklayın\nDeğişiklikler yeniden başlatıldığında görülecek + Bir radyo istasyonu eklediğinizde burada göreceksiniz + İstasyon bulunamadı! + İptal + Kaydet + Değerlendir + Başlık, sanatçı veya albüm ara + En az üç karakter girin + Albümler + Sanatçılar + Şarkılar + Düşük güvenlik + Silmek için uzun basın + Yerel URL + Sunucu Adı + Parola + Sunucu URL’si + Kullanıcı adı + İptal + Sil + Kaydet + Sunucu ekle + İptal + Girişe git + Yine de devam et + İstenen sunucuya ulaşılamıyor. Devam etmeyi seçerseniz bu iletişim penceresi bir saat boyunca tekrar görünmez. + Sunucuya ulaşılamıyor + Tempo, Subsonic için açık kaynaklı ve hafif bir müzik istemcisidir, Android için yerel olarak tasarlanıp geliştirilmiştir. + Hakkında + Her zaman açık ekran + Dönüştürme formatı + Etkinleştirildiğinde, Tempo parçayı aşağıdaki dönüştürme ayarlarıyla indirmeye zorlamaz. + İndirmelerde yayın için kullanılan sunucu ayarlarına öncelik ver + Etkinleştirildiğinde, Tempo dönüştürülmüş parçaları indirir. + Dönüştürülmüş parçaları indir + Etkinleştirildiğinde, parçanın tahmini süresi sunucudan istenir. + İçerik uzunluğunu tahmin et + İndirmeler için dönüştürme formatı + Mobilde dönüştürme formatı + Wi-Fi’da dönüştürme formatı + Etkinleştirildiğinde, Tempo parçayı aşağıdaki dönüştürme ayarlarıyla yayınlamayacaktır. + Sunucu dönüştürme ayarlarına öncelik ver + Parçanın dönüştürülmesinde öncelik sunucuya verildi + Önbellekleme stratejisi + Değişikliğin geçerli olması için uygulamayı elle yeniden başlatmalısınız. + Bir çalma listesi bittiğinde benzer şarkılar çalarak müziğin devam etmesine izin verir + Sürekli çalma + Albüm kapağı önbelleği boyutu + Veri tüketimini azaltmak için kapak görsellerinin indirilmesinden kaçının. + Mobil veri kullanımını sınırla + Devam ederseniz tüm kayıtlı öğeler geri alınamaz şekilde silinecektir. + Kayıtlı öğeleri sil + İndirme depolaması + Ses ayarlarını düzenle + Ekolayzır + https://github.com/eddyizm/tempo + Gelişmeleri takip et + Github + Görsel çözünürlüğünü ayarla + Dil + Çıkış yap + İndirmeler için bit hızı + Mobilde bit hızı + Wi-Fi’da bit hızı + Ortam dosyası önbelleği boyutu + Müzik dizinlerini göster + Etkinleştirildiğinde müzik dizini bölümü görüntülenir. Klasörlerde gezinmenin doğru çalışması için sunucunun bu özelliği desteklemesi gerekir. + Podcast’i göster + Etkinleştirildiğinde podcast bölümü görüntülenir. Tam etkili olması için uygulamayı yeniden başlatın. + Ses kalitesini göster + Her ses parçası için bit hızı ve ses formatı gösterilecektir. + Öğe değerlemesini göster + Etkinleştirildiğinde, öğenin puanı ve favori olarak işaretlenip işaretlenmediği görüntülenir. + Eşitleme zamanlayıcısı + Etkinleştirildiğinde, kullanıcı çalma sırasını kaydedebilir ve uygulamayı açtığında bu durumu yükleyebilir. + Bu kullanıcı için çalma sırasını eşitle + Radyoyu göster + Etkinleştirildiğinde radyo bölümü görüntülenir. Tam etkili olması için uygulamayı yeniden başlatın. + Yeniden çalma kazanç modunu ayarla + Yuvarlatılmış köşeler + Köşe boyutu + Eğrilik açısının büyüklüğünü ayarlar. + Etkinleştirildiğinde, tüm kapak görselleri için bir eğme açısı uygulanır. Değişiklikler yeniden başlatıldığında geçerli olur. + Kütüphaneyi tara + Müzik scrobbling özelliğini etkinleştir + Sistem dili + Müzik paylaşımını etkinleştir + Yayın önbelleği boyutu + Yayın önbellek depolaması + Scrobbling özelliğinin çalışabilmesi için sunucunun bu verileri alacak şekilde etkinleştirilmiş olması gerekir. + Bir sanatçı radyosu dinlerken, anında karışımda veya tümünü karıştırırken belirli bir kullanıcı puanının altındaki parçalar yok sayılacaktır. + Replay gain, ses parçalarının ses seviyesini ayarlayarak tutarlı bir dinleme deneyimi sağlayan bir özelliktir. Bu ayar yalnızca parçada gerekli meta veriler varsa etkilidir. + Scrobbling, cihazınızın dinlediğiniz şarkılar hakkında bilgileri müzik sunucusuna göndermesini sağlayan bir özelliktir. Bu bilgiler müzik tercihlerinize göre kişisel öneriler oluşturulmasına yardımcı olur. + Kullanıcının müziği bir bağlantı üzerinden paylaşmasına olanak tanır. Bu işlevin sunucu tarafından desteklenmesi ve etkinleştirilmesi gerekir ve yalnızca tekil parçalar, albümler ve çalma listeleriyle sınırlıdır. + Bu kullanıcının çalma sırasının durumunu geri yükler. Buna çalma sırasındaki parçalar, o anda çalan parça ve bu parçadaki konum dahildir. Sunucunun bu özelliği desteklemesi gerekir. + %1$s \nŞu anda kullanımda: %2$s MiB + Dönüştürme moduna öncelik verilir. “Doğrudan çal” olarak ayarlanırsa dosyanın bit hızı değiştirilmez. + Dönüştürülmüş medyayı indir. Etkinleştirilirse indirme uç noktası kullanılmaz, bunun yerine aşağıdaki ayarlar geçerli olur. \n\n “İndirmeler için dönüştürme formatı” “Doğrudan indir” olarak ayarlanırsa dosyanın bit hızı değiştirilmez. + Dosya anlık olarak dönüştürüldüğünde, istemci genellikle parçanın süresini göstermez. Bu işlevi destekleyen sunuculardan çalınan parçanın süresini tahmin etmeleri istenebilir, + ancak yanıt süreleri daha uzun olabilir. + Etkinleştirildiğinde, yıldızlı parçalar çevrimdışı kullanım için indirilecektir. + Çevrimdışı kullanım için yıldızlı parçaları eşitle + Tema + Veri + Genel + Değerlendirme + Replay Gain + Scrobble + Değerlendirmeye göre parçaları yok say + Şu puana sahip şarkılar: + Paylaş + Eşitleme + Dönüştürme + Dönüştürme İndir + Arayüz + Dönüştürülmüş indirme + 3.1.0 + Sürüm + Mobil ağ üzerinden yayın yapmadan önce kullanıcı onayı iste. + Yalnızca Wi-Fi ile yayın uyarısı + Bağlantıyı kopyala + Paylaşımı sil + Paylaşımı güncelle + Bitiş tarihi: %1$s + Paylaşım desteklenmiyor veya etkin değil + Açıklama + Bitiş tarihi + İptal + Kaydet + Paylaş + Çalma listesine ekle + Sıraya ekle + İndir + Albüm alınırken hata oluştu + Sanatçı alınırken hata oluştu + Albüme git + Sanatçıya git + Anında karışım + Sonra çal + Değerlendir + Kaldır + Paylaş + İndirilenler + En çok çalınan parçalar + Son eklenen parçalar + Son çalınan parçalar + Yıldızlı parçalar + %1$s’in en iyi parçaları + %1$d yılı + %1$s • %2$s %3$s + İptal + Devam et + Devam et ve indir + Yıldızlı parçaların indirilmesi yüksek miktarda veri gerektirebilir. + Yıldızlı parçaları eşitle + Değişikliklerin geçerli olması için uygulamayı yeniden başlatın. + Önbelleğe alınmış dosyaların hedefini bir depolamadan diğerine değiştirmek, önceki depolamadaki önbellek dosyalarının silinmesine yol açabilir. + Depolama seçeneğini seç + Harici + Dahili + https://buymeacoffee.com/a.cappiello + Albüm + Sanatçı + Bit derinliği + Bit hızı + İçerik türü + Tamam + Parça bilgisi + Disk numarası + Süre + Tür + Yol + Örnekleme oranı + Boyut + Uzantı + Dosya Subsonic API’leri kullanılarak indirildi. Dosyanın dönüştürme ve bit hızı, kaynak dosyadan değişmeden kaldı. + Uygulama, sunucudan dosyayı dönüştürmesini ve bit hızını değiştirmesini talep edecektir. Kullanıcının istediği kodek %1$s ve bit hızı %2$s. Seçilen formatta kodek ve bit hızındaki olası değişiklikler + sunucu tarafından yapılır ve bu işlem sunucu tarafından desteklenebilir veya desteklenmeyebilir. + Uygulama yalnızca sunucunun sağladığı orijinal dosyayı okuyacaktır. + Uygulama, dönüştürülmemiş dosyayı orijinal kaynağın bit hızıyla sunucudan açıkça talep eder. + Çalınacak dosyanın kalitesi sunucunun kararına bırakılır. Uygulama, olası dönüştürmeler için kodek ve bit hızı seçimini zorlamaz. + Uygulama, sunucudan dosyanın bit hızını değiştirmesini talep edecektir. + Kullanıcının istediği bit hızı %1$s, kaynak dosyanın kodeği aynı kalacaktır. Seçilen formatta dosyanın bit hızındaki olası değişiklikler sunucu tarafından yapılır ve bu işlem sunucu tarafından desteklenebilir veya desteklenmeyebilir. + Uygulama, sunucudan dosyayı dönüştürmesini talep edecektir. + Kullanıcının istediği kodek %1$s, bit hızı ise kaynak dosyayla aynı kalacaktır. Dosyanın seçilen formata olası dönüştürülmesi sunucuya bağlıdır, destekleyebilir ya da desteklemeyebilir. + Başlık + Parça numarası + Dönüştürülmüş içerik türü + Dönüştürülmüş uzantı + Yıl + unDraw + İllüstrasyonlarıyla bu uygulamayı daha güzel hale getirmemize yardımcı olan unDraw’a özel teşekkürler. + https://undraw.co/ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd2b066e..ef389aeb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -90,6 +90,7 @@ Downloads Select two or more filters Filter + Filter artists Filter Genres (%1$d) (+%1$d) @@ -164,6 +165,7 @@ Add Add to playlist Download all + Rate album Download All Downloaded @@ -192,6 +194,7 @@ Year %1$.2fx Clean play queue + Saved play queue Server Priority Unknown format Transcoding @@ -311,11 +314,13 @@ If enabled, show the podcast section. Restart the app for it to take full effect. Show audio quality The bitrate and audio format will be shown for each audio track. + Show song star rating + If enabled, hides 5 star rating for track on song page\n\n*Requires App restart Show item rating If enabled, the item\'s rating and whether it is marked as a favorite will be displayed. Sync timer If enabled, the user will have the ability to save their play queue and will have the ability to load state when opening the application. - Sync play queue for this user + Sync play queue for this user [Not Fully Baked] Show radio If enabled, show the radio section. Restart the app for it to take full effect. Set replay gain mode @@ -334,7 +339,7 @@ Replay gain is a feature that allows you to adjust the volume level of audio tracks for a consistent listening experience. This setting is only effective if the track contains the necessary metadata. Scrobbling is a feature that allows your device to send information about the songs you listen to the music server. This information helps create personalized recommendations based on your music preferences. Allows the user to share music via a link. The functionality must be supported and enabled server-side and is limited to individual tracks, albums and playlists. - Returns the state of the play queue for this user. This includes the tracks in the play queue, the currently playing track, and the position within this track. The server must support this feature. + Returns the state of the play queue for this user. This includes the tracks in the play queue, the currently playing track, and the position within this track. The server must support this feature.\n*This setting is not 100% working on all servers/devices. %1$s \nCurrently in use: %2$s MiB Priority given to the transcoding mode. If set to \"Direct play\" the bitrate of the file will not be changed. Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format for donwloads\" is set to \"Direct download\" the bitrate of the file will not be changed. diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml index c90779b2..ddbcc1a7 100644 --- a/app/src/main/res/xml/global_preferences.xml +++ b/app/src/main/res/xml/global_preferences.xml @@ -57,6 +57,12 @@ android:summary="@string/settings_audio_quality_summary" android:key="audio_quality_per_item" /> + + +