Merge branch 'eddyizm:development' into development

This commit is contained in:
skajmer 2025-11-13 10:23:43 +01:00 committed by GitHub
commit f7a21cbb52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 82 additions and 9 deletions

View file

@ -1,6 +1,10 @@
# Changelog # Changelog
## Pending release.. ## Pending release..
* chore: Update strings.xml by @Sevinfolds in https://github.com/eddyizm/tempus/pull/249
* fix: disallow duplicate songs in queue by @eddyizm in https://github.com/eddyizm/tempus/pull/252
* fix: github release check by @eddyizm in https://github.com/eddyizm/tempus/pull/253
* fix: Fixed crash when viewing share by @drakeerv in https://github.com/eddyizm/tempus/pull/255
## [4.2.0](https://github.com/eddyizm/tempo/releases/tag/v4.2.0) (2025-11-09) ## [4.2.0](https://github.com/eddyizm/tempo/releases/tag/v4.2.0) (2025-11-09)
## What's Changed ## What's Changed

View file

@ -11,7 +11,7 @@ android {
targetSdk 35 targetSdk 35
versionCode 5 versionCode 5
versionName '4.2.0' versionName '4.2.1'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
javaCompileOptions { javaCompileOptions {

View file

@ -7,10 +7,11 @@ public class UpdateUtil {
public static boolean showUpdateDialog(LatestRelease release) { public static boolean showUpdateDialog(LatestRelease release) {
if (release.getTagName() == null) return false; if (release.getTagName() == null) return false;
String remoteTag = release.getTagName().replaceAll("^\\D+", "");
try { try {
String[] local = BuildConfig.VERSION_NAME.split("\\."); String[] local = BuildConfig.VERSION_NAME.split("\\.");
String[] remote = release.getTagName().split("\\."); String[] remote = remoteTag.split("\\.");
for (int i = 0; i < local.length; i++) { for (int i = 0; i < local.length; i++) {
int localPart = Integer.parseInt(local[i]); int localPart = Integer.parseInt(local[i]);

View file

@ -121,6 +121,15 @@ public class QueueRepository {
} }
} }
private boolean isMediaInQueue(List<Queue> queue, Child media) {
if (queue == null || media == null) return false;
return queue.stream().anyMatch(queueItem ->
queueItem != null && media.getId() != null &&
queueItem.getId().equals(media.getId())
);
}
public void insertAll(List<Child> toAdd, boolean reset, int afterIndex) { public void insertAll(List<Child> toAdd, boolean reset, int afterIndex) {
try { try {
List<Queue> media = new ArrayList<>(); List<Queue> media = new ArrayList<>();
@ -134,8 +143,14 @@ public class QueueRepository {
media = getMediaThreadSafe.getMedia(); media = getMediaThreadSafe.getMedia();
} }
for (int i = 0; i < toAdd.size(); i++) { List<Child> filteredToAdd = toAdd;
Queue queueItem = new Queue(toAdd.get(i)); final List<Queue> finalMedia = media;
filteredToAdd = toAdd.stream()
.filter(child -> !isMediaInQueue(finalMedia, child))
.collect(Collectors.toList());
for (int i = 0; i < filteredToAdd.size(); i++) {
Queue queueItem = new Queue(filteredToAdd.get(i));
media.add(afterIndex + i, queueItem); media.add(afterIndex + i, queueItem);
} }

View file

@ -438,7 +438,7 @@ public class MainActivity extends BaseActivity {
} }
private void checkTempoUpdate() { private void checkTempoUpdate() {
if (BuildConfig.FLAVOR.equals("tempus") && Preferences.showTempoUpdateDialog()) { if (BuildConfig.FLAVOR.equals("tempus") && Preferences.isGithubUpdateEnabled() && Preferences.showTempusUpdateDialog()) {
mainViewModel.checkTempoUpdate().observe(this, latestRelease -> { mainViewModel.checkTempoUpdate().observe(this, latestRelease -> {
if (latestRelease != null && UpdateUtil.showUpdateDialog(latestRelease)) { if (latestRelease != null && UpdateUtil.showUpdateDialog(latestRelease)) {
GithubTempoUpdateDialog dialog = new GithubTempoUpdateDialog(latestRelease); GithubTempoUpdateDialog dialog = new GithubTempoUpdateDialog(latestRelease);

View file

@ -55,7 +55,7 @@ public class GithubTempoUpdateDialog extends DialogFragment {
}); });
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(v -> { alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(v -> {
Preferences.setTempoUpdateReminder(); Preferences.setTempusUpdateReminder();
Objects.requireNonNull(getDialog()).dismiss(); Objects.requireNonNull(getDialog()).dismiss();
}); });

View file

@ -29,6 +29,7 @@ import androidx.navigation.NavOptions;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference; import androidx.preference.SwitchPreference;
@ -77,6 +78,13 @@ public class SettingsFragment extends PreferenceFragmentCompat {
result -> {} result -> {}
); );
if (!BuildConfig.FLAVOR.equals("tempus")) {
PreferenceCategory githubUpdateCategory = findPreference("settings_github_update_category_key");
if (githubUpdateCategory != null) {
getPreferenceScreen().removePreference(githubUpdateCategory);
}
}
directoryPickerLauncher = registerForActivityResult( directoryPickerLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), new ActivityResultContracts.StartActivityForResult(),
result -> { result -> {

View file

@ -17,6 +17,7 @@ import androidx.media3.common.util.UnstableApi;
import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.R;
import com.cappielloantonio.tempo.glide.CustomGlideRequest; import com.cappielloantonio.tempo.glide.CustomGlideRequest;
import com.cappielloantonio.tempo.subsonic.models.Share; import com.cappielloantonio.tempo.subsonic.models.Share;
import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.ui.dialog.ShareUpdateDialog; import com.cappielloantonio.tempo.ui.dialog.ShareUpdateDialog;
import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.UIUtil; import com.cappielloantonio.tempo.util.UIUtil;
@ -24,6 +25,8 @@ import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
import com.cappielloantonio.tempo.viewmodel.ShareBottomSheetViewModel; import com.cappielloantonio.tempo.viewmodel.ShareBottomSheetViewModel;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.util.List;
@UnstableApi @UnstableApi
public class ShareBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener { public class ShareBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
@ -50,8 +53,15 @@ public class ShareBottomSheetDialog extends BottomSheetDialogFragment implements
private void init(View view) { private void init(View view) {
ImageView shareCover = view.findViewById(R.id.share_cover_image_view); ImageView shareCover = view.findViewById(R.id.share_cover_image_view);
String coverArtId = null;
List<Child> entries = shareBottomSheetViewModel.getShare().getEntries();
if (entries != null && !entries.isEmpty()) {
coverArtId = entries.get(0).getCoverArtId();
}
CustomGlideRequest.Builder CustomGlideRequest.Builder
.from(requireContext(), shareBottomSheetViewModel.getShare().getEntries().get(0).getCoverArtId(), CustomGlideRequest.ResourceType.Unknown) .from(requireContext(), coverArtId, CustomGlideRequest.ResourceType.Unknown)
.build() .build()
.into(shareCover); .into(shareCover);

View file

@ -70,6 +70,7 @@ object Preferences {
private const val SONG_RATING_PER_ITEM = "song_rating_per_item" private const val SONG_RATING_PER_ITEM = "song_rating_per_item"
private const val RATING_PER_ITEM = "rating_per_item" private const val RATING_PER_ITEM = "rating_per_item"
private const val NEXT_UPDATE_CHECK = "next_update_check" private const val NEXT_UPDATE_CHECK = "next_update_check"
private const val GITHUB_UPDATE_CHECK = "github_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 ALLOW_PLAYLIST_DUPLICATES = "allow_playlist_duplicates" private const val ALLOW_PLAYLIST_DUPLICATES = "allow_playlist_duplicates"
@ -574,15 +575,21 @@ object Preferences {
return App.getInstance().preferences.getBoolean(RATING_PER_ITEM, false) return App.getInstance().preferences.getBoolean(RATING_PER_ITEM, false)
} }
@JvmStatic @JvmStatic
fun showTempoUpdateDialog(): Boolean { fun isGithubUpdateEnabled(): Boolean {
return App.getInstance().preferences.getBoolean(GITHUB_UPDATE_CHECK, true)
}
@JvmStatic
fun showTempusUpdateDialog(): Boolean {
return App.getInstance().preferences.getLong( return App.getInstance().preferences.getLong(
NEXT_UPDATE_CHECK, 0 NEXT_UPDATE_CHECK, 0
) + 86400000 < System.currentTimeMillis() ) + 86400000 < System.currentTimeMillis()
} }
@JvmStatic @JvmStatic
fun setTempoUpdateReminder() { fun setTempusUpdateReminder() {
App.getInstance().preferences.edit().putLong(NEXT_UPDATE_CHECK, System.currentTimeMillis()).apply() App.getInstance().preferences.edit().putLong(NEXT_UPDATE_CHECK, System.currentTimeMillis()).apply()
} }

View file

@ -270,7 +270,13 @@
<string name="server_unreachable_dialog_title">Сервер недоступен</string> <string name="server_unreachable_dialog_title">Сервер недоступен</string>
<string name="settings_about_summary">Tempus — это легкий музыкальный клиент с открытым исходным кодом для Subsonic, разработанный и созданный специально для Android.</string> <string name="settings_about_summary">Tempus — это легкий музыкальный клиент с открытым исходным кодом для Subsonic, разработанный и созданный специально для Android.</string>
<string name="settings_about_title">О нас</string> <string name="settings_about_title">О нас</string>
<string name="settings_album_detail">Показать детали альбома</string>
<string name="settings_album_detail_summary">Если включено, отображать информацию об альбоме, например жанр, количество песен и т. д., на странице альбома.</string>
<string name="settings_allow_playlist_duplicates">Разрешить добавление дубликатов в плейлист</string>
<string name="settings_allow_playlist_duplicates_summary">Если включено, дубликаты не будут проверяться при добавлении в плейлист..</string>
<string name="settings_always_on_display">Всегда на дисплее</string> <string name="settings_always_on_display">Всегда на дисплее</string>
<string name="settings_artist_sort_by_album_count">Сортировать исполнителей по количеству альбомов</string>
<string name="settings_artist_sort_by_album_count_summary">Если включено, сортировать исполнителей по количеству альбомов. Если отключено, сортировать по имени.</string>
<string name="settings_audio_transcode_download_format">Формат перекодирования</string> <string name="settings_audio_transcode_download_format">Формат перекодирования</string>
<string name="settings_audio_transcode_download_priority_summary">Если этот параметр включен, Tempus не будет принудительно загружать трек с настройками перекодирования, указанными ниже.</string> <string name="settings_audio_transcode_download_priority_summary">Если этот параметр включен, Tempus не будет принудительно загружать трек с настройками перекодирования, указанными ниже.</string>
<string name="settings_audio_transcode_download_priority_title">Установите приоритет настроек сервера, используемых для потоковой передачи при загрузке</string> <string name="settings_audio_transcode_download_priority_title">Установите приоритет настроек сервера, используемых для потоковой передачи при загрузке</string>
@ -319,8 +325,12 @@
<string name="settings_queue_syncing_countdown">Таймер синхронизации</string> <string name="settings_queue_syncing_countdown">Таймер синхронизации</string>
<string name="settings_queue_syncing_summary">Если этот параметр включен, пользователь будет иметь возможность сохранять свою очередь воспроизведения и загружать состояние при открытии приложения.</string> <string name="settings_queue_syncing_summary">Если этот параметр включен, пользователь будет иметь возможность сохранять свою очередь воспроизведения и загружать состояние при открытии приложения.</string>
<string name="settings_queue_syncing_title">Синхронизировать очередь воспроизведения для этого пользователя</string> <string name="settings_queue_syncing_title">Синхронизировать очередь воспроизведения для этого пользователя</string>
<string name="settings_show_mini_shuffle_button">Показать кнопку Shuffle</string>
<string name="settings_show_mini_shuffle_button_summary">Если включено, показывать кнопку перемешивания, убрать сердечко в мини-плеере</string>
<string name="settings_radio">Показать радио</string> <string name="settings_radio">Показать радио</string>
<string name="settings_radio_summary">Если включено, показывать раздел радио. Перезапустите приложение, чтобы оно вступило в силу.</string> <string name="settings_radio_summary">Если включено, показывать раздел радио. Перезапустите приложение, чтобы оно вступило в силу.</string>
<string name="settings_auto_download_lyrics">Автоматическая загрузка текстов песен</string>
<string name="settings_auto_download_lyrics_summary">Автоматически сохранять тексты песен, когда они доступны, чтобы их можно было просматривать в автономном режиме.</string>
<string name="settings_replay_gain">Установите режим усиления воспроизведения</string> <string name="settings_replay_gain">Установите режим усиления воспроизведения</string>
<string name="settings_rounded_corner">Закругленные углы</string> <string name="settings_rounded_corner">Закругленные углы</string>
<string name="settings_rounded_corner_size">Размер углов</string> <string name="settings_rounded_corner_size">Размер углов</string>
@ -346,6 +356,9 @@
<string name="settings_sync_starred_albums_for_offline_use_title">Синхронизировать помеченные альбомы для использования в автономном режиме.</string> <string name="settings_sync_starred_albums_for_offline_use_title">Синхронизировать помеченные альбомы для использования в автономном режиме.</string>
<string name="settings_sync_starred_tracks_for_offline_use_summary">Если этот параметр включен, помеченные треки будут загружены для использования в автономном режиме.</string> <string name="settings_sync_starred_tracks_for_offline_use_summary">Если этот параметр включен, помеченные треки будут загружены для использования в автономном режиме.</string>
<string name="settings_sync_starred_tracks_for_offline_use_title">Синхронизировать помеченные треки для использования в автономном режиме.</string> <string name="settings_sync_starred_tracks_for_offline_use_title">Синхронизировать помеченные треки для использования в автономном режиме.</string>
<string name="settings_sync_starred_artists_for_offline_use_title">Синхронизировать избранных исполнителей для использования офлайн</string>
<string name="settings_support_summary">Присоединяйтесь к обсуждениям в сообществе и оказывайте поддержку</string>
<string name="settings_support_title">Поддержка пользователей</string>
<string name="settings_theme">Тема</string> <string name="settings_theme">Тема</string>
<string name="settings_title_data">Данные</string> <string name="settings_title_data">Данные</string>
<string name="settings_title_general">Общий</string> <string name="settings_title_general">Общий</string>

View file

@ -331,6 +331,9 @@
<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>
<string name="settings_support_discussion_link">https://github.com/eddyizm/tempus/discussions</string> <string name="settings_support_discussion_link">https://github.com/eddyizm/tempus/discussions</string>
<string name="settings_github_update">Updates</string>
<string name="settings_github_update_title">Check github for release updates</string>
<string name="settings_github_update_summary">If using the github version, by default app will check for new apk release. Toggle to disable automatic github checks</string>
<string name="settings_support_summary">Join community discussions and support</string> <string name="settings_support_summary">Join community discussions and support</string>
<string name="settings_support_title">User support</string> <string name="settings_support_title">User support</string>
<string name="settings_scan_result">Scanning: counting %1$d tracks</string> <string name="settings_scan_result">Scanning: counting %1$d tracks</string>

View file

@ -400,6 +400,18 @@
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:key="settings_github_update_category_key"
app:title="@string/settings_github_update">
<Preference
app:selectable="false"
app:summary="@string/settings_github_update_summary" />
<SwitchPreference
android:title="@string/settings_github_update_title"
android:defaultValue="true"
android:key="github_update_check" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/settings_about_title"> <PreferenceCategory app:title="@string/settings_about_title">
<Preference <Preference
app:selectable="false" app:selectable="false"