Compare commits
1 Commits
2a18f86051
...
1a2b98a4b6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a2b98a4b6 |
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -13,6 +13,7 @@
|
|||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="resolveExternalAnnotations" value="false" />
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -192,8 +192,7 @@
|
|||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-parcelize'
|
apply plugin: 'kotlin-parcelize'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
buildToolsVersion = '35.0.0'
|
|
||||||
|
|
||||||
compileSdk 35
|
compileSdk 35
|
||||||
|
buildToolsVersion = '35.0.0'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
@@ -75,37 +74,37 @@ dependencies {
|
|||||||
implementation files('../libs/lib-decoder-ffmpeg-release.aar')
|
implementation files('../libs/lib-decoder-ffmpeg-release.aar')
|
||||||
|
|
||||||
// AndroidX
|
// AndroidX
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
|
||||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.3.0'
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.9.6'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.8.6'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.9.6'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.8.6'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.4.0'
|
implementation 'androidx.recyclerview:recyclerview:1.4.0'
|
||||||
implementation 'androidx.room:room-runtime:2.8.4'
|
implementation 'androidx.room:room-runtime:2.6.1'
|
||||||
implementation 'androidx.core:core-splashscreen:1.2.0'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
|
|
||||||
// Android Material
|
// Android Material
|
||||||
implementation 'com.google.android.material:material:1.13.0'
|
implementation 'com.google.android.material:material:1.10.0'
|
||||||
|
|
||||||
// Glide
|
// Glide
|
||||||
implementation 'com.github.bumptech.glide:glide:5.0.5'
|
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||||
implementation 'com.github.bumptech.glide:annotations:5.0.5'
|
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||||
|
|
||||||
// Media3
|
// Media3
|
||||||
implementation 'androidx.media3:media3-session:1.8.0'
|
implementation 'androidx.media3:media3-session:1.5.1'
|
||||||
implementation 'androidx.media3:media3-common:1.8.0'
|
implementation 'androidx.media3:media3-common:1.5.1'
|
||||||
implementation 'androidx.media3:media3-exoplayer:1.8.0'
|
implementation 'androidx.media3:media3-exoplayer:1.5.1'
|
||||||
implementation 'androidx.media3:media3-ui:1.8.0'
|
implementation 'androidx.media3:media3-ui:1.5.1'
|
||||||
implementation 'androidx.media3:media3-exoplayer-hls:1.8.0'
|
implementation 'androidx.media3:media3-exoplayer-hls:1.5.1'
|
||||||
tempoImplementation 'androidx.media3:media3-cast:1.8.0'
|
tempoImplementation 'androidx.media3:media3-cast:1.5.1'
|
||||||
playImplementation 'androidx.media3:media3-cast:1.8.0'
|
playImplementation 'androidx.media3:media3-cast:1.5.1'
|
||||||
|
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:5.0.5'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||||
annotationProcessor 'androidx.room:room-compiler:2.8.4'
|
annotationProcessor 'androidx.room:room-compiler:2.6.1'
|
||||||
|
|
||||||
// Retrofit
|
// Retrofit
|
||||||
implementation 'com.squareup.retrofit2:retrofit:3.0.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.3.2'
|
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.14'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:3.0.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
|
||||||
}
|
}
|
||||||
@@ -73,29 +73,6 @@ public class PlaylistRepository {
|
|||||||
return listLivePlaylistSongs;
|
return listLivePlaylistSongs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableLiveData<Boolean> isSongInPlaylist(String playlistId, String songId) {
|
|
||||||
MutableLiveData<Boolean> songInPlayList = new MutableLiveData<>();
|
|
||||||
|
|
||||||
App.getSubsonicClientInstance(false)
|
|
||||||
.getPlaylistClient()
|
|
||||||
.getPlaylist(playlistId)
|
|
||||||
.enqueue(new Callback<ApiResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
|
||||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylist() != null && response.body().getSubsonicResponse().getPlaylist().getEntries() != null) {
|
|
||||||
List<Child> songs = response.body().getSubsonicResponse().getPlaylist().getEntries();
|
|
||||||
songInPlayList.setValue(songs.stream().anyMatch(s -> s.getId().equals(songId)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return songInPlayList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSongToPlaylist(String playlistId, ArrayList<String> songsId) {
|
public void addSongToPlaylist(String playlistId, ArrayList<String> songsId) {
|
||||||
App.getSubsonicClientInstance(false)
|
App.getSubsonicClientInstance(false)
|
||||||
.getPlaylistClient()
|
.getPlaylistClient()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class RetrofitClient(subsonic: Subsonic) {
|
|||||||
.connectTimeout(20, TimeUnit.SECONDS)
|
.connectTimeout(20, TimeUnit.SECONDS)
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
.readTimeout(30, TimeUnit.SECONDS)
|
||||||
.writeTimeout(30, TimeUnit.SECONDS)
|
.writeTimeout(30, TimeUnit.SECONDS)
|
||||||
// .addInterceptor(getHttpLoggingInterceptor())
|
.addInterceptor(getHttpLoggingInterceptor())
|
||||||
.addInterceptor(cacheUtil.offlineInterceptor)
|
.addInterceptor(cacheUtil.offlineInterceptor)
|
||||||
// .addNetworkInterceptor(cacheUtil.onlineInterceptor)
|
// .addNetworkInterceptor(cacheUtil.onlineInterceptor)
|
||||||
.cache(getCache())
|
.cache(getCache())
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.DialogPlaylistChooserBinding;
|
import com.cappielloantonio.tempo.databinding.DialogPlaylistChooserBinding;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
@@ -99,23 +98,7 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
|||||||
@Override
|
@Override
|
||||||
public void onPlaylistClick(Bundle bundle) {
|
public void onPlaylistClick(Bundle bundle) {
|
||||||
Playlist playlist = bundle.getParcelable(Constants.PLAYLIST_OBJECT);
|
Playlist playlist = bundle.getParcelable(Constants.PLAYLIST_OBJECT);
|
||||||
|
|
||||||
playlistChooserViewModel.isSongInPlaylist(Objects.requireNonNull(playlist).getId(), requireActivity()).observe(requireActivity(), songInPlaylist -> {
|
|
||||||
if (!songInPlaylist) {
|
|
||||||
playlistChooserViewModel.addSongToPlaylist(playlist.getId());
|
playlistChooserViewModel.addSongToPlaylist(playlist.getId());
|
||||||
dismiss();
|
dismiss();
|
||||||
} else {
|
|
||||||
PlaylistDuplicateSongDialog playlistDuplicateSongDialog = new PlaylistDuplicateSongDialog(playlist,
|
|
||||||
playlistChooserViewModel.getSongToAdd(),
|
|
||||||
new DialogClickCallback() {
|
|
||||||
@Override
|
|
||||||
public void onPositiveClick() {
|
|
||||||
playlistChooserViewModel.addSongToPlaylist(playlist.getId());
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
playlistDuplicateSongDialog.show(getActivity().getSupportFragmentManager(), null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package com.cappielloantonio.tempo.ui.dialog;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.Button;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.fragment.app.DialogFragment;
|
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.R;
|
|
||||||
import com.cappielloantonio.tempo.databinding.DialogPlaylistDuplicateSongBinding;
|
|
||||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
||||||
|
|
||||||
public class PlaylistDuplicateSongDialog extends DialogFragment {
|
|
||||||
|
|
||||||
private final Playlist playlist;
|
|
||||||
|
|
||||||
private final Child song;
|
|
||||||
|
|
||||||
private final DialogClickCallback dialogClickCallback;
|
|
||||||
|
|
||||||
public PlaylistDuplicateSongDialog(Playlist playlist, Child song, DialogClickCallback dialogClickCallback) {
|
|
||||||
this.playlist = playlist;
|
|
||||||
this.song = song;
|
|
||||||
this.dialogClickCallback = dialogClickCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
|
||||||
DialogPlaylistDuplicateSongBinding bind = DialogPlaylistDuplicateSongBinding.inflate(getLayoutInflater());
|
|
||||||
bind.playlistDuplicateSongDialogSummary
|
|
||||||
.setText(getResources().getString(R.string.playlist_duplicate_song_dialog_summary, song.getTitle(), playlist.getName()));
|
|
||||||
|
|
||||||
return new MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setView(bind.getRoot())
|
|
||||||
.setTitle(R.string.playlist_duplicate_song_dialog_title)
|
|
||||||
.setPositiveButton(R.string.playlist_duplicate_song_dialog_positive_button, null)
|
|
||||||
.setNegativeButton(R.string.playlist_duplicate_song_dialog_negative_button, null)
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
setButtonAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setButtonAction() {
|
|
||||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
|
||||||
|
|
||||||
if (dialog != null) {
|
|
||||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
|
||||||
positiveButton.setOnClickListener(v -> {
|
|
||||||
dialogClickCallback.onPositiveClick();
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
|
|
||||||
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
|
||||||
negativeButton.setOnClickListener(v -> {
|
|
||||||
dialogClickCallback.onNegativeClick();
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -175,7 +175,7 @@ object Preferences {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isInUseServerAddressLocal(): Boolean {
|
fun isInUseServerAddressLocal(): Boolean {
|
||||||
return getInUseServerAddress() == getLocalAddress() && !getLocalAddress().isNullOrEmpty()
|
return getInUseServerAddress() == getLocalAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|||||||
@@ -33,13 +33,6 @@ public class PlaylistChooserViewModel extends AndroidViewModel {
|
|||||||
return playlists;
|
return playlists;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Boolean> isSongInPlaylist(String playlistId, LifecycleOwner owner) {
|
|
||||||
MutableLiveData<Boolean> songs = new MutableLiveData<>();
|
|
||||||
|
|
||||||
playlistRepository.isSongInPlaylist(playlistId, toAdd.getId()).observe(owner, songs::postValue);
|
|
||||||
return songs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSongToPlaylist(String playlistId) {
|
public void addSongToPlaylist(String playlistId) {
|
||||||
playlistRepository.addSongToPlaylist(playlistId, new ArrayList(Collections.singletonList(toAdd.getId())));
|
playlistRepository.addSongToPlaylist(playlistId, new ArrayList(Collections.singletonList(toAdd.getId())));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/playlist_duplicate_song_dialog_summary"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginBottom="4dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -406,8 +406,4 @@
|
|||||||
<string name="track_info_summary_transcoding_codec">Tempo wird den Server bitten, die Datei zu transkodieren. Der vom Benutzer gewünschte Codec ist %1$s, die Bitrate wird dieselbe wie bei der original Datei sein. Die potentielle Transkodierung der Datei in das gewünschte Format ist vom Server abhängig. Dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
<string name="track_info_summary_transcoding_codec">Tempo wird den Server bitten, die Datei zu transkodieren. Der vom Benutzer gewünschte Codec ist %1$s, die Bitrate wird dieselbe wie bei der original Datei sein. Die potentielle Transkodierung der Datei in das gewünschte Format ist vom Server abhängig. Dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
||||||
<string name="track_info_summary_transcoding_bitrate">Tempo wird den Server bitten, die Bitrate der Datei zu erändern. Die vom Benutzer gewünschte Bitrate ist %1$s, der Codec der Originaldatei wird nicht verändert. Änderungen an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
<string name="track_info_summary_transcoding_bitrate">Tempo wird den Server bitten, die Bitrate der Datei zu erändern. Die vom Benutzer gewünschte Bitrate ist %1$s, der Codec der Originaldatei wird nicht verändert. Änderungen an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
||||||
<string name="track_info_summary_full_transcode">Die Anwendung wird den Server bitten die Datei zu transkodieren und die Bitrate zu verändern. Der vom Benutzer gewünschte Codec ist %1$s, mit der Bitrate %2$s. Änderungen am Codec und an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
<string name="track_info_summary_full_transcode">Die Anwendung wird den Server bitten die Datei zu transkodieren und die Bitrate zu verändern. Der vom Benutzer gewünschte Codec ist %1$s, mit der Bitrate %2$s. Änderungen am Codec und an der Bitrate der Datei werden vom Server ausgeführt, dieser kann die Operation gegebenenfalls nicht unterstützen.</string>
|
||||||
<string name="playlist_duplicate_song_dialog_title">Doppelter Track</string>
|
|
||||||
<string name="playlist_duplicate_song_dialog_summary">Der Track \"%1$s\" existiert bereits in der Playlist \"%2$s\"</string>
|
|
||||||
<string name="playlist_duplicate_song_dialog_positive_button">Trotzdem hinzufügen</string>
|
|
||||||
<string name="playlist_duplicate_song_dialog_negative_button">Nicht hinzufügen</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -416,8 +416,4 @@
|
|||||||
<string name="undraw_page">unDraw</string>
|
<string name="undraw_page">unDraw</string>
|
||||||
<string name="undraw_thanks">A special thanks goes to unDraw without whose illustrations we could not have made this application more beautiful.</string>
|
<string name="undraw_thanks">A special thanks goes to unDraw without whose illustrations we could not have made this application more beautiful.</string>
|
||||||
<string name="undraw_url">https://undraw.co/</string>
|
<string name="undraw_url">https://undraw.co/</string>
|
||||||
<string name="playlist_duplicate_song_dialog_title">Duplicate song</string>
|
|
||||||
<string name="playlist_duplicate_song_dialog_summary">The song \"%1$s\" already exists in playlist \"%2$s\".</string>
|
|
||||||
<string name="playlist_duplicate_song_dialog_positive_button">Add anyway</string>
|
|
||||||
<string name="playlist_duplicate_song_dialog_negative_button">Don\'t add</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.13.1'
|
classpath 'com.android.tools.build:gradle:8.8.0'
|
||||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.21'
|
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Wed Nov 06 17:17:57 CET 2024
|
#Wed Nov 06 17:17:57 CET 2024
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
Reference in New Issue
Block a user