mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-30 22:32:07 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73b368d202 | ||
|
|
f293a0116b | ||
|
|
ff5bec30c0 | ||
|
|
5c66bed477 | ||
|
|
d2068106e4 | ||
|
|
b7b02854d5 | ||
|
|
0edafc2d8e | ||
|
|
279302737d | ||
|
|
634de67d74 | ||
|
|
cd44368d66 | ||
|
|
ae8aa56602 | ||
|
|
34d6692dae | ||
|
|
8d2f0edbab | ||
|
|
d4d7aaba2b | ||
|
|
d690df86d8 | ||
|
|
e8f3cdbb48 | ||
|
|
33aa38e885 | ||
|
|
2faba71df0 | ||
|
|
1d3a32be5d | ||
|
|
5b8e7d1404 | ||
|
|
85a5d01e72 | ||
|
|
c7b17f2214 | ||
|
|
d8c8a783de | ||
|
|
293b0f71c8 | ||
|
|
e6e0e399e0 | ||
|
|
3223de5d03 | ||
|
|
c6d08d6a3f | ||
|
|
375501f282 | ||
|
|
0a62f0d81e | ||
|
|
12f09b2201 |
48
.github/workflows/github_release.yml
vendored
48
.github/workflows/github_release.yml
vendored
@@ -28,6 +28,13 @@ jobs:
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Setup build tool version variable
|
||||
shell: bash
|
||||
run: |
|
||||
BUILD_TOOL_VERSION=$(ls /usr/local/lib/android/sdk/build-tools/ | tail -n 1)
|
||||
echo "BUILD_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
|
||||
echo Last build tool version is: $BUILD_TOOL_VERSION
|
||||
|
||||
- name: Build APK
|
||||
id: build
|
||||
run: bash ./gradlew assembleTempoRelease
|
||||
@@ -41,6 +48,8 @@ jobs:
|
||||
alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
env:
|
||||
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
|
||||
|
||||
- name: Make artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -48,34 +57,6 @@ jobs:
|
||||
name: app-release-signed
|
||||
path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||
|
||||
# - name: Build AAB
|
||||
# run: bash ./gradlew bundleRelease
|
||||
|
||||
# - name: Sign AAB
|
||||
# id: sign_aab
|
||||
# uses: r0adkll/sign-android-release@v1
|
||||
# with:
|
||||
# releaseDirectory: app/build/outputs/bundle/release
|
||||
# signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||
# alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
# keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
# keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
|
||||
# - name: Make artifact
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: app-release-signed
|
||||
# path: ${{steps.sign_aab.outputs.signedReleaseFile}}
|
||||
|
||||
# - name: Build Changelog
|
||||
# id: changelog
|
||||
# uses: ardalanamini/auto-changelog@v3
|
||||
# with:
|
||||
# mention-authors: false
|
||||
# mention-new-contributors: false
|
||||
# include-compare: false
|
||||
# semver: false
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@@ -83,7 +64,6 @@ jobs:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release v${{ github.ref }}
|
||||
body: '> Changelog coming soon'
|
||||
# body: ${{ steps.changelog.outputs.changelog }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -96,13 +76,3 @@ jobs:
|
||||
asset_path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||
asset_name: app-tempo-release.apk
|
||||
asset_content_type: application/zip
|
||||
|
||||
# - name: Upload AAB
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ github.token }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ${{steps.sign_aab.outputs.signedReleaseFile}}
|
||||
# asset_name: app-release.aab
|
||||
# asset_content_type: application/zip
|
||||
|
||||
5
.idea/gradle.xml
generated
5
.idea/gradle.xml
generated
@@ -4,16 +4,15 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="17" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
@@ -10,6 +10,9 @@ android {
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 34
|
||||
|
||||
versionCode 24
|
||||
versionName '3.7.0'
|
||||
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
javaCompileOptions {
|
||||
@@ -28,15 +31,11 @@ android {
|
||||
tempo {
|
||||
dimension = "default"
|
||||
applicationId 'com.cappielloantonio.tempo'
|
||||
versionCode 23
|
||||
versionName '3.6.0'
|
||||
}
|
||||
|
||||
notquitemy {
|
||||
dimension = "default"
|
||||
applicationId "com.cappielloantonio.notquitemy.tempo"
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +65,6 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
// AndroidX
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||
@@ -80,18 +77,18 @@ dependencies {
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
|
||||
// Android Material
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
implementation 'com.google.android.material:material:1.10.0'
|
||||
|
||||
// Glide
|
||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||
|
||||
// Media3
|
||||
implementation 'androidx.media3:media3-session:1.2.0'
|
||||
implementation 'androidx.media3:media3-common:1.2.0'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.2.0'
|
||||
implementation 'androidx.media3:media3-ui:1.2.0'
|
||||
tempoImplementation 'androidx.media3:media3-cast:1.2.0'
|
||||
implementation 'androidx.media3:media3-session:1.2.1'
|
||||
implementation 'androidx.media3:media3-common:1.2.1'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.2.1'
|
||||
implementation 'androidx.media3:media3-ui:1.2.1'
|
||||
tempoImplementation 'androidx.media3:media3-cast:1.2.1'
|
||||
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||
annotationProcessor 'androidx.room:room-compiler:2.6.1'
|
||||
|
||||
@@ -8,7 +8,9 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Genre;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@@ -39,7 +41,7 @@ public class GenreRepository {
|
||||
if (size != -1) {
|
||||
genres.setValue(genreList.subList(0, Math.min(size, genreList.size())));
|
||||
} else {
|
||||
genres.setValue(genreList);
|
||||
genres.setValue(genreList.stream().sorted(Comparator.comparing(Genre::getGenre)).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,10 +104,10 @@ public class SongRepository {
|
||||
return randomSongsSample;
|
||||
}
|
||||
|
||||
public void scrobble(String id) {
|
||||
public void scrobble(String id, boolean submission) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.scrobble(id)
|
||||
.scrobble(id, submission)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
@@ -31,9 +31,10 @@ public class DownloaderManager {
|
||||
|
||||
private final Context context;
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final HashMap<String, Download> downloads;
|
||||
private final DownloadIndex downloadIndex;
|
||||
|
||||
private static HashMap<String, Download> downloads;
|
||||
|
||||
public DownloaderManager(Context context, DataSource.Factory dataSourceFactory, DownloadManager downloadManager) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
@@ -61,19 +62,11 @@ public class DownloaderManager {
|
||||
}
|
||||
|
||||
public boolean isDownloaded(MediaItem mediaItem) {
|
||||
@Nullable Download download = downloads.get(mediaItem.mediaId);
|
||||
return download != null && download.state != Download.STATE_FAILED;
|
||||
return isDownloaded(mediaItem.mediaId);
|
||||
}
|
||||
|
||||
public boolean areDownloaded(List<MediaItem> mediaItems) {
|
||||
for (MediaItem mediaItem : mediaItems) {
|
||||
@Nullable Download download = downloads.get(mediaItem.mediaId);
|
||||
if (download != null && download.state != Download.STATE_FAILED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return mediaItems.stream().anyMatch(this::isDownloaded);
|
||||
}
|
||||
|
||||
public void download(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||
@@ -92,6 +85,7 @@ public class DownloaderManager {
|
||||
public void remove(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||
DownloadService.sendRemoveDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem).id, false);
|
||||
deleteDatabase(download.getId());
|
||||
downloads.remove(download.getId());
|
||||
}
|
||||
|
||||
public void remove(List<MediaItem> mediaItems, List<com.cappielloantonio.tempo.model.Download> downloads) {
|
||||
@@ -122,23 +116,33 @@ public class DownloaderManager {
|
||||
return download != null ? download.getTitle() : null;
|
||||
}
|
||||
|
||||
public static void updateRequestDownload(Download download) {
|
||||
updateDatabase(download.request.id);
|
||||
downloads.put(download.request.id, download);
|
||||
}
|
||||
|
||||
public static void removeRequestDownload(Download download) {
|
||||
deleteDatabase(download.request.id);
|
||||
downloads.remove(download.request.id);
|
||||
}
|
||||
|
||||
private static DownloadRepository getDownloadRepository() {
|
||||
return new DownloadRepository();
|
||||
}
|
||||
|
||||
public static void insertDatabase(com.cappielloantonio.tempo.model.Download download) {
|
||||
private static void insertDatabase(com.cappielloantonio.tempo.model.Download download) {
|
||||
getDownloadRepository().insert(download);
|
||||
}
|
||||
|
||||
public static void deleteDatabase(String id) {
|
||||
private static void deleteDatabase(String id) {
|
||||
getDownloadRepository().delete(id);
|
||||
}
|
||||
|
||||
public static void deleteAllDatabase() {
|
||||
private static void deleteAllDatabase() {
|
||||
getDownloadRepository().deleteAll();
|
||||
}
|
||||
|
||||
public static void updateDatabase(String id) {
|
||||
private static void updateDatabase(String id) {
|
||||
getDownloadRepository().update(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||
notification = notificationHelper.buildDownloadCompletedNotification(context, R.drawable.ic_check_circle, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||
notification = Notification.Builder.recoverBuilder(context, notification).setGroup(DownloadUtil.DOWNLOAD_NOTIFICATION_SUCCESSFUL_GROUP).build();
|
||||
NotificationUtil.setNotification(this.context, successfulDownloadGroupNotificationId, successfulDownloadGroupNotification);
|
||||
DownloaderManager.updateDatabase(download.request.id);
|
||||
DownloaderManager.updateRequestDownload(download);
|
||||
} else if (download.state == Download.STATE_FAILED) {
|
||||
notification = notificationHelper.buildDownloadFailedNotification(context, R.drawable.ic_error, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||
notification = Notification.Builder.recoverBuilder(context, notification).setGroup(DownloadUtil.DOWNLOAD_NOTIFICATION_FAILED_GROUP).build();
|
||||
@@ -109,7 +109,7 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||
|
||||
@Override
|
||||
public void onDownloadRemoved(@NonNull DownloadManager downloadManager, Download download) {
|
||||
DownloaderManager.deleteDatabase(download.request.id);
|
||||
DownloaderManager.removeRequestDownload(download);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,9 +293,9 @@ public class MediaManager {
|
||||
getQueueRepository().setPlayingPausedTimestamp(mediaItem.mediaId, ms);
|
||||
}
|
||||
|
||||
public static void scrobble(MediaItem mediaItem) {
|
||||
public static void scrobble(MediaItem mediaItem, boolean submission) {
|
||||
if (mediaItem != null && Preferences.isScrobblingEnabled()) {
|
||||
getSongRepository().scrobble(mediaItem.mediaMetadata.extras.getString("id"));
|
||||
getSongRepository().scrobble(mediaItem.mediaMetadata.extras.getString("id"), submission);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import retrofit2.Call;
|
||||
|
||||
public class MediaAnnotationClient {
|
||||
private static final String TAG = "BrowsingClient";
|
||||
private static final String TAG = "MediaAnnotationClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final MediaAnnotationService mediaAnnotationService;
|
||||
@@ -34,8 +34,8 @@ public class MediaAnnotationClient {
|
||||
return mediaAnnotationService.setRating(subsonic.getParams(), id, rating);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> scrobble(String id) {
|
||||
public Call<ApiResponse> scrobble(String id, boolean submission) {
|
||||
Log.d(TAG, "scrobble()");
|
||||
return mediaAnnotationService.scrobble(subsonic.getParams(), id);
|
||||
return mediaAnnotationService.scrobble(subsonic.getParams(), id, submission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@ public interface MediaAnnotationService {
|
||||
Call<ApiResponse> setRating(@QueryMap Map<String, String> params, @Query("id") String id, @Query("rating") int rating);
|
||||
|
||||
@GET("scrobble")
|
||||
Call<ApiResponse> scrobble(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||
Call<ApiResponse> scrobble(@QueryMap Map<String, String> params, @Query("id") String id, @Query("submission") Boolean submission);
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
|
||||
@Keep
|
||||
class ApiResponse {
|
||||
@SerializedName("subsonic-response")
|
||||
lateinit var subsonicResponse: SubsonicResponse;
|
||||
lateinit var subsonicResponse: SubsonicResponse
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
@@ -37,6 +38,7 @@ public class BaseActivity extends AppCompatActivity {
|
||||
initializeDownloader();
|
||||
checkBatteryOptimization();
|
||||
checkPermission();
|
||||
checkAlwaysOnDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,6 +68,12 @@ public class BaseActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAlwaysOnDisplay() {
|
||||
if (Preferences.isDisplayAlwaysOn()) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean detectBatteryOptimization() {
|
||||
String packageName = getPackageName();
|
||||
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.List;
|
||||
|
||||
public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAdapter.ViewHolder> implements Filterable {
|
||||
private final ClickCallback click;
|
||||
private String currentFilter;
|
||||
|
||||
private final Filter filtering = new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
@@ -32,6 +34,7 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
filteredList.addAll(albumsFull);
|
||||
} else {
|
||||
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||
currentFilter = filterPattern;
|
||||
|
||||
for (AlbumID3 item : albumsFull) {
|
||||
if (item.getName().toLowerCase().contains(filterPattern)) {
|
||||
@@ -48,8 +51,7 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
albums.clear();
|
||||
albums.addAll((List) results.values);
|
||||
albums = (List<AlbumID3>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
@@ -60,6 +62,8 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
public AlbumCatalogueAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.albums = Collections.emptyList();
|
||||
this.albumsFull = Collections.emptyList();
|
||||
this.currentFilter = "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -92,9 +96,8 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
}
|
||||
|
||||
public void setItems(List<AlbumID3> albums) {
|
||||
this.albums = albums;
|
||||
this.albumsFull = new ArrayList<>(albums);
|
||||
notifyDataSetChanged();
|
||||
filtering.filter(currentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,4 +165,4 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
private String filterValue;
|
||||
|
||||
private List<Child> songs;
|
||||
private List<Child> shuffling;
|
||||
private List<Child> grouped;
|
||||
|
||||
public DownloadHorizontalAdapter(ClickCallback click) {
|
||||
@@ -82,6 +83,7 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
|
||||
this.songs = songs;
|
||||
this.grouped = groupSong(songs);
|
||||
this.shuffling = shufflingSong(new ArrayList<>(songs));
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
@@ -90,6 +92,10 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
return grouped.get(id);
|
||||
}
|
||||
|
||||
public List<Child> getShuffling() {
|
||||
return shuffling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return position;
|
||||
@@ -136,6 +142,27 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
return songs;
|
||||
}
|
||||
|
||||
private List<Child> shufflingSong(List<Child> songs) {
|
||||
if (filterValue == null) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
switch (filterKey) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
return songs.stream().filter(child -> child.getId().equals(filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getAlbumId(), filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getGenre(), filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getArtistId(), filterValue)).collect(Collectors.toList());
|
||||
default:
|
||||
return songs;
|
||||
}
|
||||
}
|
||||
|
||||
private String countSong(String filterKey, String filterValue, List<Child> songs) {
|
||||
if (filterValue != null) {
|
||||
switch (filterKey) {
|
||||
|
||||
@@ -49,7 +49,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
Child song = songs.get(position);
|
||||
|
||||
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(this.showAlbum ? song.getAlbum() : song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration() != null ? song.getDuration() : 0, false)));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(this.showAlbum ? song.getAlbum() : song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
||||
holder.item.trackNumberTextView.setText(MusicUtil.getReadableTrackNumber(holder.itemView.getContext(), song.getTrack()));
|
||||
|
||||
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
|
||||
|
||||
@@ -31,7 +31,8 @@ import java.util.Objects;
|
||||
public class PlaylistEditorDialog extends DialogFragment {
|
||||
private DialogPlaylistEditorBinding bind;
|
||||
private PlaylistEditorViewModel playlistEditorViewModel;
|
||||
private PlaylistCallback playlistCallback;
|
||||
|
||||
private final PlaylistCallback playlistCallback;
|
||||
|
||||
private String playlistName;
|
||||
private PlaylistDialogSongHorizontalAdapter playlistDialogSongHorizontalAdapter;
|
||||
|
||||
@@ -20,7 +20,8 @@ import java.util.Objects;
|
||||
public class PodcastChannelEditorDialog extends DialogFragment {
|
||||
private DialogPodcastChannelEditorBinding bind;
|
||||
private PodcastChannelEditorViewModel podcastChannelEditorViewModel;
|
||||
private PodcastCallback podcastCallback;
|
||||
|
||||
private final PodcastCallback podcastCallback;
|
||||
|
||||
private String channelUrl;
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ import java.util.Objects;
|
||||
public class RadioEditorDialog extends DialogFragment {
|
||||
private DialogRadioEditorBinding bind;
|
||||
private RadioEditorViewModel radioEditorViewModel;
|
||||
private RadioCallback radioCallback;
|
||||
|
||||
private final RadioCallback radioCallback;
|
||||
|
||||
private String radioName;
|
||||
private String radioStreamURL;
|
||||
|
||||
@@ -17,7 +17,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
public class TrackInfoDialog extends DialogFragment {
|
||||
private DialogTrackInfoBinding bind;
|
||||
private MediaMetadata mediaMetadata;
|
||||
|
||||
private final MediaMetadata mediaMetadata;
|
||||
|
||||
public TrackInfoDialog(MediaMetadata mediaMetadata) {
|
||||
this.mediaMetadata = mediaMetadata;
|
||||
@@ -28,7 +29,7 @@ public class TrackInfoDialog extends DialogFragment {
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogTrackInfoBinding.inflate(getLayoutInflater());
|
||||
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
return new MaterialAlertDialogBuilder(requireActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setPositiveButton(R.string.track_info_dialog_positive_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
|
||||
@@ -52,6 +52,12 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
||||
initData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
albumCatalogueViewModel.stopLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
@@ -73,7 +79,7 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private void initData() {
|
||||
albumCatalogueViewModel = new ViewModelProvider(requireActivity()).get(AlbumCatalogueViewModel.class);
|
||||
albumCatalogueViewModel.loadAlbums(500);
|
||||
albumCatalogueViewModel.loadAlbums();
|
||||
}
|
||||
|
||||
private void initAppBar() {
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.cappielloantonio.tempo.ui.adapter.ArtistSimilarAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.ArtistPageViewModel;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
@@ -104,7 +105,12 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
|
||||
bind.albumsHorizontalRecyclerView.setVisibility(isHorizontalRecyclerViewVisible ? View.GONE : View.VISIBLE);
|
||||
bind.albumsVerticalRecyclerView.setVisibility(isHorizontalRecyclerViewVisible ? View.VISIBLE : View.GONE);
|
||||
|
||||
Preferences.setArtistAlbumLayout(!isHorizontalRecyclerViewVisible);
|
||||
});
|
||||
|
||||
bind.albumsHorizontalRecyclerView.setVisibility(Preferences.isArtistAlbumLayoutHorizontal() ? View.VISIBLE : View.GONE);
|
||||
bind.albumsVerticalRecyclerView.setVisibility(Preferences.isArtistAlbumLayoutHorizontal() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private void initAppBar() {
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -165,6 +166,19 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
||||
bind.downloadedGoBackImageView.setVisibility(stack.size() > 1 ? View.VISIBLE : View.GONE);
|
||||
|
||||
setupBackPressing(stack.size());
|
||||
setupShuffleButton();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupShuffleButton() {
|
||||
bind.shuffleDownloadedTextViewClickable.setOnClickListener(view -> {
|
||||
List<Child> songs = downloadHorizontalAdapter.getShuffling();
|
||||
|
||||
if (songs != null && !songs.isEmpty()) {
|
||||
Collections.shuffle(songs);
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0);
|
||||
activity.setBottomSheetInPeek(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
@@ -91,6 +92,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
actionSyncStarredTracks();
|
||||
actionChangeDownloadStorage();
|
||||
actionDeleteDownloadStorage();
|
||||
actionKeepScreenOn();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,4 +250,17 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void actionKeepScreenOn() {
|
||||
findPreference("always_on_display").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,16 +128,18 @@ public class MusicUtil {
|
||||
}
|
||||
|
||||
|
||||
public static String getReadableDurationString(long duration, boolean millis) {
|
||||
public static String getReadableDurationString(Long duration, boolean millis) {
|
||||
long lenght = duration != null ? duration : 0;
|
||||
|
||||
long minutes;
|
||||
long seconds;
|
||||
|
||||
if (millis) {
|
||||
minutes = (duration / 1000) / 60;
|
||||
seconds = (duration / 1000) % 60;
|
||||
minutes = (lenght / 1000) / 60;
|
||||
seconds = (lenght / 1000) % 60;
|
||||
} else {
|
||||
minutes = duration / 60;
|
||||
seconds = duration % 60;
|
||||
minutes = lenght / 60;
|
||||
seconds = lenght % 60;
|
||||
}
|
||||
|
||||
if (minutes < 60) {
|
||||
@@ -149,6 +151,11 @@ public class MusicUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getReadableDurationString(Integer duration, boolean millis) {
|
||||
long lenght = duration != null ? duration : 0;
|
||||
return getReadableDurationString(lenght, millis);
|
||||
}
|
||||
|
||||
public static String getReadablePodcastDurationString(long duration) {
|
||||
long minutes = duration / 60;
|
||||
|
||||
@@ -320,4 +327,4 @@ public class MusicUtil {
|
||||
|
||||
toFilter.addAll(filtered);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ object Preferences {
|
||||
private const val BUFFERING_STRATEGY = "buffering_strategy"
|
||||
private const val SKIP_MIN_STAR_RATING = "skip_min_star_rating"
|
||||
private const val MIN_STAR_RATING = "min_star_rating"
|
||||
private const val ARTIST_ALBUM_LAYOUT = "artist_album_layout"
|
||||
private const val ALWAYS_ON_DISPLAY = "always_on_display"
|
||||
|
||||
|
||||
@JvmStatic
|
||||
@@ -345,4 +347,20 @@ object Preferences {
|
||||
fun getMinStarRatingAccepted(): Int {
|
||||
return App.getInstance().preferences.getInt(MIN_STAR_RATING, 0)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isArtistAlbumLayoutHorizontal(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(ARTIST_ALBUM_LAYOUT, true)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setArtistAlbumLayout(isArtistAlbumLayoutHorizontal: Boolean) {
|
||||
App.getInstance().preferences.edit().putBoolean(ARTIST_ALBUM_LAYOUT, isArtistAlbumLayoutHorizontal)
|
||||
.apply()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isDisplayAlwaysOn(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(ALWAYS_ON_DISPLAY, false)
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
private final MutableLiveData<List<AlbumID3>> albumList = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
private int page = 0;
|
||||
private Status status = Status.STOPPED;
|
||||
|
||||
public AlbumCatalogueViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
@@ -31,7 +32,18 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
return albumList;
|
||||
}
|
||||
|
||||
public void loadAlbums(int size) {
|
||||
public void loadAlbums() {
|
||||
page = 0;
|
||||
status = Status.RUNNING;
|
||||
albumList.setValue(new ArrayList<>());
|
||||
loadAlbums(500);
|
||||
}
|
||||
|
||||
public void stopLoading() {
|
||||
status = Status.STOPPED;
|
||||
}
|
||||
|
||||
private void loadAlbums(int size) {
|
||||
retrieveAlbums(new MediaCallback() {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
@@ -39,15 +51,19 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
|
||||
@Override
|
||||
public void onLoadMedia(List<?> media) {
|
||||
List<AlbumID3> liveAlbum = albumList.getValue();
|
||||
if (status == Status.STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (liveAlbum == null) liveAlbum = new ArrayList<>();
|
||||
List<AlbumID3> liveAlbum = albumList.getValue();
|
||||
|
||||
liveAlbum.addAll((List<AlbumID3>) media);
|
||||
albumList.setValue(liveAlbum);
|
||||
|
||||
if (media.size() == size) {
|
||||
loadAlbums(size);
|
||||
} else {
|
||||
status = Status.STOPPED;
|
||||
}
|
||||
}
|
||||
}, size, size * page++);
|
||||
@@ -73,4 +89,9 @@ public class AlbumCatalogueViewModel extends AndroidViewModel {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private enum Status {
|
||||
RUNNING,
|
||||
STOPPED
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ public class IndexViewModel extends AndroidViewModel {
|
||||
|
||||
private MusicFolder musicFolder;
|
||||
|
||||
private MutableLiveData<Indexes> indexes = new MutableLiveData<>(null);
|
||||
|
||||
public IndexViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
|
||||
|
||||
@@ -76,11 +76,20 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/download_title_section"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/downloaded_go_back_image_view"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/shuffle_downloaded_text_view_clickable"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/download_shuffle_all_subtitle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/downloaded_text_view_refreshable"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/downloaded_go_back_image_view"
|
||||
android:layout_width="24dp"
|
||||
@@ -108,12 +117,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/downloaded_text_view_refreshable" />
|
||||
app:layout_constraintTop_toBottomOf="@id/shuffle_downloaded_text_view_clickable" />
|
||||
|
||||
<include
|
||||
android:id="@+id/download_downloaded_placeholder"
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
android:id="@+id/downloaded_item_more_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:paddingVertical="12dp"
|
||||
android:paddingStart="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/item_cover_image_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/item_cover_image_view" >
|
||||
|
||||
@@ -144,14 +144,14 @@
|
||||
|
||||
<string-array name="replay_gain_titles">
|
||||
<item>Deaktiviert</item>
|
||||
<item>Track</item>
|
||||
<item>Titel</item>
|
||||
<item>Album</item>
|
||||
<item>Auto</item>
|
||||
<item>Automatisch</item>
|
||||
</string-array>
|
||||
<string-array name="replay_gain_values">
|
||||
<item>disabled</item>
|
||||
<item>track</item>
|
||||
<item>album</item>
|
||||
<item>auto</item>
|
||||
<item>Deaktiviert</item>
|
||||
<item>Titel</item>
|
||||
<item>Album</item>
|
||||
<item>Automatisch</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -10,6 +10,7 @@
|
||||
<string name="album_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||
<string name="album_bottom_sheet_remove_all">Alle entfernen</string>
|
||||
<string name="album_bottom_sheet_shuffle">Mischen</string>
|
||||
<string name="album_bottom_sheet_share">Teilen</string>
|
||||
<string name="album_catalogue_title">Alben</string>
|
||||
<string name="album_catalogue_title_expanded">Alben durchsuchen</string>
|
||||
<string name="album_error_retrieving_artist">Error retrieving artist</string>
|
||||
@@ -36,6 +37,7 @@
|
||||
<string name="artist_list_page_title">Künstler</string>
|
||||
<string name="artist_page_radio_button">Radio</string>
|
||||
<string name="artist_page_shuffle_button">Mischen</string>
|
||||
<string name="artist_page_switch_layout_button">Layout umschalten</string>
|
||||
<string name="artist_page_title_album_more_like_this_button">Ähnliches</string>
|
||||
<string name="artist_page_title_album_section">Alben</string>
|
||||
<string name="artist_page_title_biography_more_button">Mehr</string>
|
||||
@@ -219,12 +221,14 @@
|
||||
<string name="settings_audio_transcode_download_priority_title">Transkodierungseinstellungen des Servers für Downloads bevorzugen.</string>
|
||||
<string name="settings_audio_transcode_download_summary">Diese Option aktiviert das Transkodieren für heruntergeladene Tracks.</string>
|
||||
<string name="settings_audio_transcode_download_title">Transkodierte Tracks herunterladen</string>
|
||||
<string name="settings_audio_transcode_format_download">Transkodierungs-Format für Downloads</string>
|
||||
<string name="settings_audio_transcode_format_mobile">Transkodierungsformat im mobilen Netz</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Transkodierungsformat im Wi-Fi</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_summary">Diese Option bittet den Server um die geschätzte Länge des Titels.</string>
|
||||
<string name="settings_audio_transcode_estimate_content_length_title">Titellänge schätzen</string>
|
||||
<string name="settings_audio_transcode_priority_summary">Diese Option deaktiviert die weiter unten folgenden Transkodierungseinstellungen.</string>
|
||||
<string name="settings_audio_transcode_priority_title">Transkodierungseinstellungen des Servers bevorzugen</string>
|
||||
<string name="settings_audio_transcode_priority_toast">Servereinstellungen zur Transkodierung des Tracks werden bevorzugt</string>
|
||||
<string name="settings_audio_transcode_format_download">Transkodierungs-Format für Downloads</string>
|
||||
<string name="settings_audio_transcode_format_mobile">Transkodierungsformat im mobilen Netz</string>
|
||||
<string name="settings_audio_transcode_format_wifi">Transkodierungsformat im Wi-Fi</string>
|
||||
<string name="settings_covers_cache">Größe des Artwork Caches</string>
|
||||
<string name="settings_data_saving_mode_summary">Um das Datenvolumen zu begrenzen werden keine Cover heruntergeladen.</string>
|
||||
<string name="settings_data_saving_mode_title">Mobile Datennutzung begrenzen</string>
|
||||
@@ -258,9 +262,12 @@
|
||||
<string name="settings_rounded_corner_size_summary">Definiert den Eckenradius.</string>
|
||||
<string name="settings_rounded_corner_summary">Abgerundete Ecken für alle gerenderten Cover. Anwendungsneustart ist notwendig.</string>
|
||||
<string name="settings_scan_title">Sammlung scannen</string>
|
||||
<string name="settings_share_title">Teilen von Musik aktivieren</string>
|
||||
<string name="settings_summary_replay_gain">Replay-Gain ist ein Feature, das die Lautstärke von Tracks für ein konsistentes Hörerlebnis anpasst. Diese Einstellung funktioniert nur, wenn Tracks die entsprechenden Metadaten haben.</string>
|
||||
<string name="settings_summary_share">Diese Option erlaubt es dem Benutzer, Musik mit einem Link zu teilen. Die Funktionalität muss vom Server unterstützt und aktiviert sein und ist auf einzelne Titel, Alben und Wiedergabelisten beschränkt.</string>
|
||||
<string name="settings_summary_syncing">Den Zustand der Warteschlange synchronisieren. Das beinhaltet die Tracks in der Warteschlange, den aktuell gespielten Track und die Position innerhalb dieses Tracks. Der Server muss dieses Feature unterstützen.</string>
|
||||
<string name="settings_summary_transcoding">Priorität des Transkodierungsmodus. \"Direktes Abspielen\" ändert die Bitrate der Dateien nicht.</string>
|
||||
<string name="settings_summary_transcoding_estimate_content_length">When der Titel während des Abspielens transkodiert wird, When the file is transcoded on the fly, zeigt die App normalerweise keine Titellänge an. Es ist möglich einen Server, der dies unterstützt, zu bitten, die Titellänge zu schätzen. Die Antwortzeiten könnten sich verlängern.</string>
|
||||
<string name="settings_summary_transcoding_download">Transkodierte Medien herunterladen. Diese Option deaktiviert den Download-Endpoint und benutzt stattdessen die folgenden Einstellungen. \n\n If \"Transkodierungs-Format\" ist auf \"Direktes Abspielen\" gesetzt, Die Bitrate des Tracks wird nicht geändert.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">Lieblingslieder werden automatisch heruntergeladen.</string>
|
||||
<string name="settings_sync_starred_tracks_for_offline_use_title">Lieblingslieder für Offline-Modus sychronisieren</string>
|
||||
@@ -268,6 +275,7 @@
|
||||
<string name="settings_title_data">Daten</string>
|
||||
<string name="settings_title_general">Allgemein</string>
|
||||
<string name="settings_title_replay_gain">Replay Gain</string>
|
||||
<string name="settings_title_share">Teilen</string>
|
||||
<string name="settings_title_syncing">Sychronisierung</string>
|
||||
<string name="settings_title_transcoding">Transkodierung</string>
|
||||
<string name="settings_title_transcoding_download">Transkodierung Download</string>
|
||||
@@ -276,6 +284,16 @@
|
||||
<string name="settings_version_title">Version</string>
|
||||
<string name="settings_wifi_only_summary">Um Erlaubnis fragen bevor über das mobile Netzwerk gestreamed wird.</string>
|
||||
<string name="settings_wifi_only_title">Warnung bei Streamen ohne Wi-Fi</string>
|
||||
<string name="share_bottom_sheet_copy_link">Link kopieren</string>
|
||||
<string name="share_bottom_sheet_delete">Share löschen</string>
|
||||
<string name="share_bottom_sheet_update">Share aktualisieren</string>
|
||||
<string name="share_subtitle_item">Ablaufdatum: %1$s</string>
|
||||
<string name="share_update_dialog_negative_button">Abbrechen</string>
|
||||
<string name="share_update_dialog_positive_button">Sichern</string>
|
||||
<string name="share_update_dialog_hint_description">Beschreibung</string>
|
||||
<string name="share_update_dialog_hint_expiration_date">Ablaufdatum</string>
|
||||
<string name="share_update_dialog_title">Teilen</string>
|
||||
<string name="share_unsupported_error">Sharing ist nicht aktiviert oder unterstützt.</string>
|
||||
<string name="song_bottom_sheet_add_to_playlist">Zu Playliste hinzufügen</string>
|
||||
<string name="song_bottom_sheet_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||
<string name="song_bottom_sheet_download">Download</string>
|
||||
@@ -283,6 +301,7 @@
|
||||
<string name="song_bottom_sheet_error_retrieving_artist">Fehler beim Abruf des Künstlers</string>
|
||||
<string name="song_bottom_sheet_go_to_album">Zum Album gehen</string>
|
||||
<string name="song_bottom_sheet_go_to_artist">Zum Künstler gehen</string>
|
||||
<string name="song_bottom_sheet_share">Teilen</string>
|
||||
<string name="song_bottom_sheet_instant_mix">Sofort-Mix</string>
|
||||
<string name="song_bottom_sheet_play_next">Nächsten Titel spielen</string>
|
||||
<string name="song_bottom_sheet_rate">Bewerten</string>
|
||||
@@ -303,4 +322,27 @@
|
||||
<string name="undraw_page">unDraw</string>
|
||||
<string name="undraw_thanks">Besonders möchten wir uns bei unDraw bedanken, durch deren Illustrationen wir diese App so schön machen konnten.</string>
|
||||
<string name="undraw_url">https://undraw.co/</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Titel Info</string>
|
||||
<string name="track_info_title">Titel</string>
|
||||
<string name="track_info_album">Album</string>
|
||||
<string name="track_info_artist">Künstler</string>
|
||||
<string name="track_info_track_number">Track Nummer</string>
|
||||
<string name="track_info_year">Jahr</string>
|
||||
<string name="track_info_genre">Genre</string>
|
||||
<string name="track_info_size">Größe</string>
|
||||
<string name="track_info_content_type">Inhaltstyp</string>
|
||||
<string name="track_info_suffix">Suffix</string>
|
||||
<string name="track_info_transcoded_content_type">Transkodierter Inhaltstyp</string>
|
||||
<string name="track_info_transcoded_suffix">Transkodiertes Suffix</string>
|
||||
<string name="track_info_duration">Länge</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_path">Pfad</string>
|
||||
<string name="track_info_disc_number">Disk Nummer</string>
|
||||
<string name="track_info_summary_downloaded_file">Diese Datei wurde mit den Subsonic APIs heruntergeladen. Der Codec und die Bitrate sind unverändert zur original Datei.</string>
|
||||
<string name="track_info_summary_server_prioritized">Die Qualität des abzuspielenden Titels wird vom Server bestimmt. Tempo stellt keinen Codec und keine Bitrate für eine potentielle Transkodierung sicher.</string>
|
||||
<string name="track_info_summary_original_file">Tempo wird nur die original Datei, so wie sie vom Server geliefert wird, lesen. Die Anwendung wird den Server explizit nach einer nicht-transkodierten Version der Datei mit der Bitrate der original Datei fragen.</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_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>
|
||||
</resources>
|
||||
@@ -65,6 +65,7 @@
|
||||
<string name="download_info_empty_title">No downloads yet!</string>
|
||||
<string name="download_item_multiple_subtitle_formatter">%1$s • %2$s items</string>
|
||||
<string name="download_item_single_subtitle_formatter">%1$s items</string>
|
||||
<string name="download_shuffle_all_subtitle">Shuffle all</string>
|
||||
<string name="download_storage_dialog_sub_summary">For the changes to take effect, restart the app.</string>
|
||||
<string name="download_storage_dialog_summary">Changing the destination of downloaded files from one storage to another will result in the immediate deletion of any previously downloaded files in the other storage.</string>
|
||||
<string name="download_storage_dialog_title">Select storage option</string>
|
||||
@@ -223,6 +224,7 @@
|
||||
<string name="server_unreachable_dialog_title">Server unreachable</string>
|
||||
<string name="settings_about_summary">Tempo is an open source and lightweight music client for Subsonic, designed and built natively for Android.</string>
|
||||
<string name="settings_about_title">About</string>
|
||||
<string name="settings_always_on_display">Always on display</string>
|
||||
<string name="settings_audio_transcode_download_format">Transcode format</string>
|
||||
<string name="settings_audio_transcode_download_priority_summary">If enabled, Tempo will not force download the track with the transcode settings below.</string>
|
||||
<string name="settings_audio_transcode_download_priority_title">Prioritize server settings used for streaming in downloads</string>
|
||||
@@ -259,12 +261,12 @@
|
||||
<string name="settings_music_directory">Show music directories</string>
|
||||
<string name="settings_music_directory_summary">If enabled, show the music directory section. Please note that for folder navigation to work properly, the server must support this feature.</string>
|
||||
<string name="settings_podcast">Show podcast</string>
|
||||
<string name="settings_podcast_summary">If enabled, show the podcast section.</string>
|
||||
<string name="settings_podcast_summary">If enabled, show the podcast section. Restart the app for it to take full effect.</string>
|
||||
<string name="settings_queue_syncing_countdown">Sync timer</string>
|
||||
<string name="settings_queue_syncing_summary">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.</string>
|
||||
<string name="settings_queue_syncing_title">Sync play queue for this user</string>
|
||||
<string name="settings_radio">Show radio</string>
|
||||
<string name="settings_radio_summary">If enabled, show the radio section.</string>
|
||||
<string name="settings_radio_summary">If enabled, show the radio section. Restart the app for it to take full effect.</string>
|
||||
<string name="settings_replay_gain">Set replay gain mode</string>
|
||||
<string name="settings_rounded_corner">Rounded corners</string>
|
||||
<string name="settings_rounded_corner_size">Corners size</string>
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
app:title="@string/settings_theme"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_always_on_display"
|
||||
android:defaultValue="false"
|
||||
android:key="always_on_display" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/settings_rounded_corner"
|
||||
android:defaultValue="true"
|
||||
|
||||
@@ -183,6 +183,8 @@ class MediaService : MediaLibraryService() {
|
||||
player.currentMediaItem,
|
||||
player.currentPosition
|
||||
)
|
||||
} else {
|
||||
MediaManager.scrobble(player.currentMediaItem, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +194,7 @@ class MediaService : MediaLibraryService() {
|
||||
playbackState == Player.STATE_ENDED &&
|
||||
player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
|
||||
) {
|
||||
MediaManager.scrobble(player.currentMediaItem)
|
||||
MediaManager.scrobble(player.currentMediaItem, true)
|
||||
MediaManager.saveChronology(player.currentMediaItem)
|
||||
}
|
||||
}
|
||||
@@ -206,7 +208,7 @@ class MediaService : MediaLibraryService() {
|
||||
|
||||
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.scrobble(oldPosition.mediaItem)
|
||||
MediaManager.scrobble(oldPosition.mediaItem, true)
|
||||
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||
}
|
||||
|
||||
|
||||
@@ -128,16 +128,19 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||
player.currentMediaItem,
|
||||
player.currentPosition
|
||||
)
|
||||
} else {
|
||||
MediaManager.scrobble(player.currentMediaItem, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
super.onPlaybackStateChanged(playbackState)
|
||||
|
||||
if (!player.hasNextMediaItem() &&
|
||||
playbackState == Player.STATE_ENDED &&
|
||||
player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
|
||||
) {
|
||||
MediaManager.scrobble(player.currentMediaItem)
|
||||
MediaManager.scrobble(player.currentMediaItem, true)
|
||||
MediaManager.saveChronology(player.currentMediaItem)
|
||||
}
|
||||
}
|
||||
@@ -151,7 +154,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||
|
||||
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
|
||||
MediaManager.scrobble(oldPosition.mediaItem)
|
||||
MediaManager.scrobble(oldPosition.mediaItem, true)
|
||||
MediaManager.saveChronology(oldPosition.mediaItem)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register('clean', Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
Reference in New Issue
Block a user