diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d0478c..76ba8bb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,27 @@ # Changelog ## Pending release.. -* fix: reverts change causing album disc/track list to get out of order by @eddyizm in https://github.com/eddyizm/tempus/pull/237 + +## [4.2.0](https://github.com/eddyizm/tempo/releases/tag/v4.2.0) (2025-11-09) +## What's Changed +* fix: Equalizer fix in main build variant by @jaime-grj in https://github.com/eddyizm/tempus/pull/239 +* fix: Images not filling holder by @eddyizm in https://github.com/eddyizm/tempus/pull/244 +* feat: Make artist and album clickable by @eddyizm in https://github.com/eddyizm/tempus/pull/243 +* feat: implement scroll to currently playing feature by @shrapnelnet in https://github.com/eddyizm/tempus/pull/247 +* fix: shuffling genres only queuing 25 songs by @shrapnelnet in https://github.com/eddyizm/tempus/pull/246 + +## New Contributors +* @shrapnelnet made their first contribution in https://github.com/eddyizm/tempus/pull/247 + +**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.1.3...v4.2.0 + +## [4.1.3](https://github.com/eddyizm/tempo/releases/tag/v4.1.3) (2025-11-06) +## What's Changed +* [fix: equalizer missing referenced value](https://github.com/eddyizm/tempus/commit/923cfd5bc97ed7db28c90348e3619d0a784fc434) +* Fix: Album track list bug by @eddyizm in https://github.com/eddyizm/tempus/pull/237 * fix: Add listener to enable equalizer when audioSessionId changes by @jaime-grj in https://github.com/eddyizm/tempus/pull/235 -**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.1.0...v4.1.2 +**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.1.0...v4.1.3 ## [4.1.0](https://github.com/eddyizm/tempo/releases/tag/v4.1.0) (2025-11-05) ## What's Changed diff --git a/app/build.gradle b/app/build.gradle index 8d6363d7..d883de1b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { minSdkVersion 24 targetSdk 35 - versionCode 4 - versionName '4.1.3' + versionCode 5 + versionName '4.2.0' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' javaCompileOptions { diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java index 85ceed53..a40b3c97 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java +++ b/app/src/main/java/com/cappielloantonio/tempo/repository/SongRepository.java @@ -100,6 +100,33 @@ public class SongRepository { return randomSongsSample; } + public MutableLiveData> getRandomSampleWithGenre(int number, Integer fromYear, Integer toYear, String genre) { + MutableLiveData> randomSongsSample = new MutableLiveData<>(); + + App.getSubsonicClientInstance(false) + .getAlbumSongListClient() + .getRandomSongs(number, fromYear, toYear, genre) + .enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + List songs = new ArrayList<>(); + + if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getRandomSongs() != null && response.body().getSubsonicResponse().getRandomSongs().getSongs() != null) { + songs.addAll(response.body().getSubsonicResponse().getRandomSongs().getSongs()); + } + + randomSongsSample.setValue(songs); + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + } + }); + + return randomSongsSample; + } + public void scrobble(String id, boolean submission) { App.getSubsonicClientInstance(false) .getMediaAnnotationClient() diff --git a/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListClient.java b/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListClient.java index d0874a5d..0a799d4e 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListClient.java +++ b/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListClient.java @@ -34,6 +34,11 @@ public class AlbumSongListClient { return albumSongListService.getRandomSongs(subsonic.getParams(), size, fromYear, toYear); } + public Call getRandomSongs(int size, Integer fromYear, Integer toYear, String genre) { + Log.d(TAG, "getRandomSongs()"); + return albumSongListService.getRandomSongs(subsonic.getParams(), size, fromYear, toYear, genre); + } + public Call getSongsByGenre(String genre, int count, int offset) { Log.d(TAG, "getSongsByGenre()"); return albumSongListService.getSongsByGenre(subsonic.getParams(), genre, count, offset); diff --git a/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListService.java b/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListService.java index 1ca7267a..e0d8995a 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListService.java +++ b/app/src/main/java/com/cappielloantonio/tempo/subsonic/api/albumsonglist/AlbumSongListService.java @@ -19,6 +19,9 @@ public interface AlbumSongListService { @GET("getRandomSongs") Call getRandomSongs(@QueryMap Map params, @Query("size") int size, @Query("fromYear") Integer fromYear, @Query("toYear") Integer toYear); + @GET("getRandomSongs") + Call getRandomSongs(@QueryMap Map params, @Query("size") int size, @Query("fromYear") Integer fromYear, @Query("toYear") Integer toYear, @Query("genre") String genre); + @GET("getSongsByGenre") Call getSongsByGenre(@QueryMap Map params, @Query("genre") String genre, @Query("count") int count, @Query("offset") int offset); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DiscoverSongAdapter.java b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DiscoverSongAdapter.java index 6da8554c..c9b21d88 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DiscoverSongAdapter.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DiscoverSongAdapter.java @@ -73,6 +73,11 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter onClick()); + + itemView.setOnLongClickListener(v -> { + onLongClick(); + return true; + }); } public void onClick() { @@ -82,6 +87,13 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter { Collections.shuffle(songs); - MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(25, songs.size())), 0); + MediaManager.startQueue(mediaBrowserListenableFuture, songs.subList(0, Math.min(500, songs.size())), 0); activity.setBottomSheetInPeek(true); }); } diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongListPageViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongListPageViewModel.java index d2396f61..acd95b1c 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongListPageViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongListPageViewModel.java @@ -37,7 +37,7 @@ public class SongListPageViewModel extends AndroidViewModel { public int year = 0; public int maxNumberByYear = 500; - public int maxNumberByGenre = 100; + public int maxNumberByGenre = 500; public SongListPageViewModel(@NonNull Application application) { super(application); @@ -51,7 +51,7 @@ public class SongListPageViewModel extends AndroidViewModel { switch (title) { case Constants.MEDIA_BY_GENRE: - songList = songRepository.getSongsByGenre(genre.getGenre(), 0); + songList = songRepository.getRandomSampleWithGenre(maxNumberByGenre, 0, 3000, genre.getGenre()); break; case Constants.MEDIA_BY_ARTIST: songList = artistRepository.getTopSongs(artist.getName(), 50); diff --git a/app/src/main/res/layout/item_home_discover_song.xml b/app/src/main/res/layout/item_home_discover_song.xml index f2d3bb9b..6e4ccee2 100644 --- a/app/src/main/res/layout/item_home_discover_song.xml +++ b/app/src/main/res/layout/item_home_discover_song.xml @@ -7,7 +7,10 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2dafec49..80af298c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -379,7 +379,7 @@ 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. + Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format for downloads\" is set to \"Direct download\" the bitrate of the file will not be changed. When the file is transcoded on the fly, the client usually does not show the track length. It is possible to request the servers that support the functionality to estimate the duration of the track being played, but the response times may take longer. If enabled, starred artists will be downloaded for offline use. Sync starred artists for offline use diff --git a/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt index d43fb61f..52fa8c1d 100644 --- a/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt +++ b/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt @@ -161,16 +161,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { 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]) - } - } + attachEqualizerIfPossible(audioSessionId) } private fun initializePlayer() { @@ -334,6 +325,10 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { override fun onRepeatModeChanged(repeatMode: Int) { Preferences.setRepeatMode(repeatMode) } + + override fun onAudioSessionIdChanged(audioSessionId: Int) { + attachEqualizerIfPossible(audioSessionId) + } }) if (player.isPlaying) { scheduleWidgetUpdates() @@ -451,6 +446,22 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { player.playWhenReady = isPlaying player.prepare() } + + private fun attachEqualizerIfPossible(audioSessionId: Int): Boolean { + if (audioSessionId == 0 || audioSessionId == -1) return false + val attached = equalizerManager.attachToSession(audioSessionId) + if (attached) { + 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]) + } + sendBroadcast(Intent(ACTION_EQUALIZER_UPDATED)) + } + return attached + } } private const val WIDGET_UPDATE_INTERVAL_MS = 1000L diff --git a/fastlane/metadata/android/en-US/changelogs/5.txt b/fastlane/metadata/android/en-US/changelogs/5.txt new file mode 100644 index 00000000..a325892f --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/5.txt @@ -0,0 +1,5 @@ +Equalizer fix in main build variant +fix Images not filling holder in discovery +Make artist and album clickable on home discovery +Implement scroll to currently playing feature +Shuffling genres now queuing 500 tracks vs 25 diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 1bd732ce..957b128f 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -8,7 +8,6 @@ Features - Streaming and Offline Mode: Stream music directly from your Subsonic server. Offline mode is currently under active development and may have limitations when using multiple servers. - Playlist Management: Create, edit, and manage playlists to curate your perfect music collection. - Gapless Playback: Experience uninterrupted playback with gapless listening mode. -- Chromecast Support: Stream your music to Chromecast devices. The support is currently in a rudimentary state. - Scrobbling Integration: Optionally integrate Tempus with Last.fm or Listenbrainz.org to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server. - Podcasts and Radio: If your Subsonic server supports it, listen to podcasts and radio shows directly within Tempus, expanding your audio entertainment options. - Transcoding Support: Activate transcoding of tracks on your Subsonic server, allowing you to set a transcoding profile for optimized streaming directly from the app. This feature requires support from your Subsonic server. @@ -16,3 +15,5 @@ Features - Equalizer: Option to use in app equalizer. - Widget: New widget to keeping the basic controls on your screen at all times. - Available in 11 languages: Currently in Chinese, French, German, Italian, Korean, Polish, Portuguese, Russion, Spanish and Turkish + +*Chromecast/Android Auto Not Supported: These features require google libraries and are only available in the github apk.* \ No newline at end of file