From 88c2129cd48b32bc99e64ce86147a8e343d61596 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sat, 28 Feb 2026 09:07:59 -0800 Subject: [PATCH 1/7] chore: bumping version for release --- CHANGELOG.md | 16 ++++++++++++++++ app/build.gradle | 4 ++-- .../metadata/android/en-US/changelogs/21.txt | 7 +++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/21.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index f48c4d66..ce3e6ed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## What's Changed +## [4.12.0](https://github.com/eddyizm/tempo/releases/tag/v4.12.0) (2026-02-28) +* chore(i18n): Update Polish translation by @skajmer in https://github.com/eddyizm/tempus/pull/441 +* feat: radio logos support for AndroidAuto by @dmachard in https://github.com/eddyizm/tempus/pull/435 +* feat: Port remove song of playlist from tempus ng by @tvillega in https://github.com/eddyizm/tempus/pull/457 +* fix: artist sort by name case sensitive by @tvillega in https://github.com/eddyizm/tempus/pull/462 +* feat: added slide out enhanced navigation for tab mode and optionally portrait mode by @tvillega in https://github.com/eddyizm/tempus/pull/450 +* feat: Android Auto: improve media service browsing by @MaFo-28 in https://github.com/eddyizm/tempus/pull/437 +* feat: Support specifying a client certificate for mTLS auth by @tinsukE in https://github.com/eddyizm/tempus/pull/458 + +## New Contributors +* @MaFo-28 made their first contribution in https://github.com/eddyizm/tempus/pull/437 +* @tinsukE made their first contribution in https://github.com/eddyizm/tempus/pull/458 + +**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.11.0...v4.12.0 + ## What's Changed ## [4.11.0](https://github.com/eddyizm/tempo/releases/tag/v4.11.0) (2026-02-15) * fix: added dynamic application id from gradle variant by @eddyizm in https://github.com/eddyizm/tempus/pull/425 diff --git a/app/build.gradle b/app/build.gradle index f8a8b106..9ae26f82 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { minSdkVersion 24 targetSdk 35 - versionCode 20 - versionName '4.11.0' + versionCode 21 + versionName '4.12.0' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' javaCompileOptions { diff --git a/fastlane/metadata/android/en-US/changelogs/21.txt b/fastlane/metadata/android/en-US/changelogs/21.txt new file mode 100644 index 00000000..793e84c5 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/21.txt @@ -0,0 +1,7 @@ +chore(i18n): Update Polish translation +feat: radio logos support for AndroidAuto +feat: Port remove song of playlist from tempus ng +fix: artist sort by name case sensitive +feat: added slide out enhanced navigation for tab mode and optionally portrait mode +feat: Android Auto: improve media service browsing +feat: Support specifying a client certificate for mTLS auth \ No newline at end of file From c7f2524085704ecba3caded76afe4cc864ccf4c3 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 2 Mar 2026 00:36:03 -0300 Subject: [PATCH 2/7] feat: feat: advertise existing long press to refresh per section (#467) * feat: advertise existing long press to refresh per section --------- Co-authored-by: eddyizm --- app/build.gradle | 1 + .../tempo/ui/fragment/LibraryFragment.java | 42 +++++ app/src/main/res/layout/fragment_library.xml | 146 ++++++++++++++---- 3 files changed, 159 insertions(+), 30 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9ae26f82..410809ce 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -101,6 +101,7 @@ dependencies { implementation 'androidx.room:room-runtime:2.6.1' implementation 'androidx.core:core-splashscreen:1.0.1' implementation 'androidx.appcompat:appcompat:1.7.0' + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0" // Android Material implementation 'com.google.android.material:material:1.10.0' diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/LibraryFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/LibraryFragment.java index daa5d412..b10e6402 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/LibraryFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/LibraryFragment.java @@ -9,6 +9,8 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ViewModelProvider; import androidx.media3.common.util.UnstableApi; import androidx.media3.session.MediaBrowser; @@ -16,8 +18,11 @@ import androidx.media3.session.SessionToken; import androidx.navigation.Navigation; import android.content.ComponentName; +import android.widget.Toast; + import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.databinding.FragmentLibraryBinding; @@ -43,6 +48,7 @@ import java.util.Objects; @UnstableApi public class LibraryFragment extends Fragment implements ClickCallback { private static final String TAG = "LibraryFragment"; + private static final String TOAST_MSG = "Long press to refresh" ; private FragmentLibraryBinding bind; private MainActivity activity; @@ -81,6 +87,7 @@ public class LibraryFragment extends Fragment implements ClickCallback { initArtistView(); initGenreView(); initPlaylistView(); + initSwipeToRefresh(); } @Override @@ -112,22 +119,41 @@ public class LibraryFragment extends Fragment implements ClickCallback { activity.navController.navigate(R.id.action_libraryFragment_to_playlistCatalogueFragment, bundle); }); + // Album bind.albumCatalogueSampleTextViewRefreshable.setOnLongClickListener(view -> { libraryViewModel.refreshAlbumSample(getViewLifecycleOwner()); return true; }); + bind.albumCatalogueSampleTextViewRefreshable.setOnClickListener( v -> + Toast.makeText(requireContext(), TOAST_MSG, Toast.LENGTH_SHORT).show() + ); + + // Artist bind.artistCatalogueSampleTextViewRefreshable.setOnLongClickListener(view -> { libraryViewModel.refreshArtistSample(getViewLifecycleOwner()); return true; }); + bind.artistCatalogueSampleTextViewRefreshable.setOnClickListener( v -> + Toast.makeText(requireContext(), TOAST_MSG, Toast.LENGTH_SHORT).show() + ); + + // Genre bind.genreCatalogueSampleTextViewRefreshable.setOnLongClickListener(view -> { libraryViewModel.refreshGenreSample(getViewLifecycleOwner()); return true; }); + bind.genreCatalogueSampleTextViewRefreshable.setOnClickListener(v -> + Toast.makeText(requireContext(), TOAST_MSG, Toast.LENGTH_SHORT).show() + ); + + // Playlist bind.playlistCatalogueSampleTextViewRefreshable.setOnLongClickListener(view -> { libraryViewModel.refreshPlaylistSample(getViewLifecycleOwner()); return true; }); + bind.playlistCatalogueSampleTextViewRefreshable.setOnClickListener( v -> + Toast.makeText(requireContext(), TOAST_MSG, Toast.LENGTH_SHORT).show() + ); } private void initAppBar() { @@ -304,4 +330,20 @@ public class LibraryFragment extends Fragment implements ClickCallback { private void initializeMediaBrowser() { mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync(); } + + public void initSwipeToRefresh() { + bind.swipeLibraryToRefresh.setOnRefreshListener(() -> { + pullToRefresh(); + bind.swipeLibraryToRefresh.setRefreshing(false); + }); + } + + private void pullToRefresh() { + LifecycleOwner lifecycleOwner = getViewLifecycleOwner(); + libraryViewModel.refreshAlbumSample(lifecycleOwner); + libraryViewModel.refreshGenreSample(lifecycleOwner); + libraryViewModel.refreshArtistSample(lifecycleOwner); + libraryViewModel.refreshPlaylistSample(lifecycleOwner); + + } } diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index c96fadaf..9c5f0419 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -11,6 +11,12 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> + + - + + + + + + + + + + android:layout_height="0dp" + android:layout_weight="1" /> @@ -130,22 +156,41 @@ android:paddingEnd="8dp" android:paddingBottom="8dp"> - + + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + + + + + + @@ -184,25 +229,45 @@ android:paddingEnd="8dp" android:paddingBottom="8dp"> - + + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + + + + + + + - + + + + + + + + + + android:layout_height="0dp" + android:layout_weight="1" /> @@ -270,4 +355,5 @@ + \ No newline at end of file From 0487f3bb9b989b51332b01a2496e4c9d3f7d6f3d Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 1 Mar 2026 19:36:48 -0800 Subject: [PATCH 3/7] fix: returns filtered list and reset correctly (#476) --- .../tempo/ui/adapter/PlaylistHorizontalAdapter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/PlaylistHorizontalAdapter.java b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/PlaylistHorizontalAdapter.java index baa4005e..763e39e7 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/PlaylistHorizontalAdapter.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/PlaylistHorizontalAdapter.java @@ -47,6 +47,7 @@ public class PlaylistHorizontalAdapter extends RecyclerView.Adapter 0) playlists.addAll((List) results.values); + if (results.values != null) { + playlists.addAll((List) results.values); + } notifyDataSetChanged(); } }; From c9cf86acb576b4f084ae0b75516f4a425d8edefe Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 2 Mar 2026 00:48:15 -0300 Subject: [PATCH 4/7] feat: toggle player bitrate visibility on touch (#466) * feat: touch player chip to toggle bitrate visibility * feat: player bitrate visibility is remembered * fix: player landscape layout not grouping chip with textview * feat: touch bitrate to toggle its visibility This catches the edge case where the the chip is not reachable due to insuficient horizontal space --------- Co-authored-by: eddyizm --- .../ui/fragment/PlayerControllerFragment.java | 27 +++++++++- .../tempo/util/Preferences.kt | 11 ++++ ...nner_fragment_player_controller_layout.xml | 50 ++++++++++++------- ...nner_fragment_player_controller_layout.xml | 50 ++++++++++++------- 4 files changed, 98 insertions(+), 40 deletions(-) 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 3bba6183..3c14788b 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,9 +8,11 @@ import android.os.Bundle; import android.os.IBinder; import android.text.TextUtils; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.Button; import android.widget.ImageButton; import android.widget.LinearLayout; @@ -33,6 +35,10 @@ import androidx.media3.session.SessionToken; import androidx.navigation.NavController; import androidx.navigation.NavOptions; import androidx.navigation.fragment.NavHostFragment; +import androidx.transition.ChangeBounds; +import androidx.transition.Slide; +import androidx.transition.TransitionManager; +import androidx.transition.TransitionSet; import androidx.viewpager2.widget.ViewPager2; import com.cappielloantonio.tempo.R; @@ -56,7 +62,6 @@ import com.google.android.material.elevation.SurfaceColors; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -314,7 +319,7 @@ public class PlayerControllerFragment extends Fragment { if (!samplingRate.trim().isEmpty()) items.add(samplingRate); String mediaQuality = TextUtils.join(" • ", items); - playerMediaBitrate.setVisibility(View.VISIBLE); + playerMediaBitrate.setVisibility(Preferences.getBitrateVisible() ? View.VISIBLE : View.GONE); playerMediaBitrate.setText(isLocal ? mediaQuality : mediaQuality); } } @@ -335,7 +340,25 @@ public class PlayerControllerFragment extends Fragment { TrackInfoDialog dialog = new TrackInfoDialog(mediaMetadata); dialog.show(activity.getSupportFragmentManager(), null); }); + + playerMediaExtension.setOnClickListener( v -> toggleBitrateVisibility() ); + playerMediaBitrate.setOnClickListener(v -> toggleBitrateVisibility() ); } + + private void toggleBitrateVisibility() { + ViewGroup parent = (ViewGroup) playerMediaBitrate.getParent(); + + TransitionSet transition = new TransitionSet() + .addTransition(new Slide(Gravity.START)) + .addTransition(new ChangeBounds()) + .setDuration(500) + .setInterpolator(new AccelerateDecelerateInterpolator()); + TransitionManager.beginDelayedTransition(parent, transition); + + playerMediaBitrate.setVisibility(Preferences.getBitrateVisible() ? View.GONE : View.VISIBLE); + Preferences.setBitrateVisible(!Preferences.getBitrateVisible()); + } + private void updateAssetLinkChips(MediaMetadata mediaMetadata) { if (assetLinkChipGroup == null) return; String mediaType = mediaMetadata.extras != null ? mediaMetadata.extras.getString("type", Constants.MEDIA_TYPE_MUSIC) : Constants.MEDIA_TYPE_MUSIC; 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 1edc914e..099a471d 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt @@ -25,6 +25,7 @@ object Preferences { private const val IN_USE_SERVER_ADDRESS = "in_use_server_address" private const val NEXT_SERVER_SWITCH = "next_server_switch" private const val PLAYBACK_SPEED = "playback_speed" + private const val BITRATE_VISIBLE = "bitrate_visible" private const val SKIP_SILENCE = "skip_silence" private const val SHUFFLE_MODE = "shuffle_mode" private const val REPEAT_MODE = "repeat_mode" @@ -292,6 +293,16 @@ object Preferences { App.getInstance().preferences.edit().putFloat(PLAYBACK_SPEED, playbackSpeed).apply() } + @JvmStatic + fun getBitrateVisible(): Boolean { + return App.getInstance().preferences.getBoolean(BITRATE_VISIBLE, true) + } + + @JvmStatic + fun setBitrateVisible(bitrateVisible: Boolean) { + App.getInstance().preferences.edit().putBoolean(BITRATE_VISIBLE, bitrateVisible).apply() + } + @JvmStatic fun isSkipSilenceMode(): Boolean { return App.getInstance().preferences.getBoolean(SKIP_SILENCE, false) 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 435e3648..b2757648 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 @@ -24,30 +24,42 @@ app:layout_constraintStart_toEndOf="@+id/vertical_guideline" app:layout_constraintTop_toTopOf="parent"> - + app:layout_constraintEnd_toEndOf="parent"> - + + + + + - + app:layout_constraintEnd_toEndOf="parent"> - + + + + + Date: Sun, 1 Mar 2026 19:59:13 -0800 Subject: [PATCH 5/7] chore: bumped version and change log for release --- CHANGELOG.md | 8 ++++++++ app/build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/22.txt | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/22.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index ce3e6ed1..5e72e383 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## What's Changed +## [4.12.3](https://github.com/eddyizm/tempo/releases/tag/v4.12.3) (2026-03-01) +* feat: advertise existing long press to refresh per section on library page by @tvillega in https://github.com/eddyizm/tempus/pull/467 +* fix: playlist filter returns properly filtered list and reset correctly by @eddyizm in https://github.com/eddyizm/tempus/pull/476 +* feat: toggle player bitrate visibility on touch by @tvillega in https://github.com/eddyizm/tempus/pull/466 + +**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.12.0...v4.12.3 + ## What's Changed ## [4.12.0](https://github.com/eddyizm/tempo/releases/tag/v4.12.0) (2026-02-28) * chore(i18n): Update Polish translation by @skajmer in https://github.com/eddyizm/tempus/pull/441 diff --git a/app/build.gradle b/app/build.gradle index 410809ce..8017646b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { minSdkVersion 24 targetSdk 35 - versionCode 21 - versionName '4.12.0' + versionCode 22 + versionName '4.12.3' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' javaCompileOptions { diff --git a/fastlane/metadata/android/en-US/changelogs/22.txt b/fastlane/metadata/android/en-US/changelogs/22.txt new file mode 100644 index 00000000..98006a09 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/22.txt @@ -0,0 +1,3 @@ +feat: advertise existing long press to refresh per section on library page +fix: playlist filter returns properly filtered list and reset correctly +feat: toggle player bitrate visibility on touch From 10c2172be040152e464344daba658d793bc4c397 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 1 Mar 2026 20:19:05 -0800 Subject: [PATCH 6/7] fix: updated constraints causing fata lint build failures (#478) --- .../layout-land/inner_fragment_player_controller_layout.xml | 4 ++-- .../res/layout/inner_fragment_player_controller_layout.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 b2757648..b337e6fd 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 @@ -70,8 +70,8 @@ android:background="?attr/selectableItemBackgroundBorderless" android:scaleType="fitCenter" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/player_media_extension" - app:layout_constraintBottom_toBottomOf="@id/player_media_extension" + app:layout_constraintTop_toTopOf="@id/player_media_quality_sector_center" + app:layout_constraintBottom_toBottomOf="@id/player_media_quality_sector_center" app:srcCompat="@drawable/ic_info_stream" app:tint="?attr/colorOnPrimaryContainer" /> diff --git a/app/src/main/res/layout/inner_fragment_player_controller_layout.xml b/app/src/main/res/layout/inner_fragment_player_controller_layout.xml index bfde20ea..05bfe253 100644 --- a/app/src/main/res/layout/inner_fragment_player_controller_layout.xml +++ b/app/src/main/res/layout/inner_fragment_player_controller_layout.xml @@ -79,8 +79,8 @@ android:background="?attr/selectableItemBackgroundBorderless" android:scaleType="fitCenter" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/player_media_extension" - app:layout_constraintBottom_toBottomOf="@id/player_media_extension" + app:layout_constraintTop_toTopOf="@id/player_media_quality_sector_center" + app:layout_constraintBottom_toBottomOf="@id/player_media_quality_sector_center" app:srcCompat="@drawable/ic_info_stream" app:tint="?attr/colorOnPrimaryContainer" /> From 3086a8b9f9148537b7f8a5c8f3e135228db19a2a Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 1 Mar 2026 20:20:08 -0800 Subject: [PATCH 7/7] chore: bumped version for build fix --- CHANGELOG.md | 2 +- app/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e72e383..6e35c44f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## What's Changed -## [4.12.3](https://github.com/eddyizm/tempo/releases/tag/v4.12.3) (2026-03-01) +## [4.12.4](https://github.com/eddyizm/tempo/releases/tag/v4.12.4) (2026-03-01) * feat: advertise existing long press to refresh per section on library page by @tvillega in https://github.com/eddyizm/tempus/pull/467 * fix: playlist filter returns properly filtered list and reset correctly by @eddyizm in https://github.com/eddyizm/tempus/pull/476 * feat: toggle player bitrate visibility on touch by @tvillega in https://github.com/eddyizm/tempus/pull/466 diff --git a/app/build.gradle b/app/build.gradle index 8017646b..cc66453b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,7 +11,7 @@ android { targetSdk 35 versionCode 22 - versionName '4.12.3' + versionName '4.12.4' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' javaCompileOptions {