mirror of
https://github.com/antebudimir/tempus.git
synced 2026-04-16 08:47:24 +00:00
feat: Add metadata caching and proper integration for external media files
This commit is contained in:
parent
24864637f9
commit
682f63ef38
17 changed files with 515 additions and 136 deletions
|
|
@ -1,11 +1,14 @@
|
|||
package com.cappielloantonio.tempo.ui.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
|
|
@ -13,7 +16,10 @@ import androidx.annotation.NonNull;
|
|||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
|
@ -56,6 +62,7 @@ public class MainActivity extends BaseActivity {
|
|||
private BottomSheetBehavior bottomSheetBehavior;
|
||||
|
||||
ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver;
|
||||
private Intent pendingDownloadPlaybackIntent;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
|
@ -77,6 +84,8 @@ public class MainActivity extends BaseActivity {
|
|||
checkConnectionType();
|
||||
getOpenSubsonicExtensions();
|
||||
checkTempoUpdate();
|
||||
|
||||
maybeSchedulePlaybackIntent(getIntent());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -84,6 +93,7 @@ public class MainActivity extends BaseActivity {
|
|||
super.onStart();
|
||||
pingServer();
|
||||
initService();
|
||||
consumePendingPlaybackIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -99,6 +109,14 @@ public class MainActivity extends BaseActivity {
|
|||
bind = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
maybeSchedulePlaybackIntent(intent);
|
||||
consumePendingPlaybackIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED)
|
||||
|
|
@ -418,4 +436,68 @@ public class MainActivity extends BaseActivity {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeSchedulePlaybackIntent(Intent intent) {
|
||||
if (intent == null) return;
|
||||
if (Constants.ACTION_PLAY_EXTERNAL_DOWNLOAD.equals(intent.getAction())
|
||||
|| intent.hasExtra(Constants.EXTRA_DOWNLOAD_URI)) {
|
||||
pendingDownloadPlaybackIntent = new Intent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void consumePendingPlaybackIntent() {
|
||||
if (pendingDownloadPlaybackIntent == null) return;
|
||||
Intent intent = pendingDownloadPlaybackIntent;
|
||||
pendingDownloadPlaybackIntent = null;
|
||||
playDownloadedMedia(intent);
|
||||
}
|
||||
|
||||
private void playDownloadedMedia(Intent intent) {
|
||||
String uriString = intent.getStringExtra(Constants.EXTRA_DOWNLOAD_URI);
|
||||
if (TextUtils.isEmpty(uriString)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = Uri.parse(uriString);
|
||||
String mediaId = intent.getStringExtra(Constants.EXTRA_DOWNLOAD_MEDIA_ID);
|
||||
if (TextUtils.isEmpty(mediaId)) {
|
||||
mediaId = uri.toString();
|
||||
}
|
||||
|
||||
String title = intent.getStringExtra(Constants.EXTRA_DOWNLOAD_TITLE);
|
||||
String artist = intent.getStringExtra(Constants.EXTRA_DOWNLOAD_ARTIST);
|
||||
String album = intent.getStringExtra(Constants.EXTRA_DOWNLOAD_ALBUM);
|
||||
int duration = intent.getIntExtra(Constants.EXTRA_DOWNLOAD_DURATION, 0);
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("id", mediaId);
|
||||
extras.putString("title", title);
|
||||
extras.putString("artist", artist);
|
||||
extras.putString("album", album);
|
||||
extras.putString("uri", uri.toString());
|
||||
extras.putString("type", Constants.MEDIA_TYPE_MUSIC);
|
||||
extras.putInt("duration", duration);
|
||||
|
||||
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder()
|
||||
.setExtras(extras)
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true);
|
||||
|
||||
if (!TextUtils.isEmpty(title)) metadataBuilder.setTitle(title);
|
||||
if (!TextUtils.isEmpty(artist)) metadataBuilder.setArtist(artist);
|
||||
if (!TextUtils.isEmpty(album)) metadataBuilder.setAlbumTitle(album);
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(mediaId)
|
||||
.setMediaMetadata(metadataBuilder.build())
|
||||
.setUri(uri)
|
||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||
.setRequestMetadata(new MediaItem.RequestMetadata.Builder()
|
||||
.setMediaUri(uri)
|
||||
.setExtras(extras)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
MediaManager.playDownloadedMediaItem(getMediaBrowserListenableFuture(), mediaItem);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import com.cappielloantonio.tempo.R;
|
|||
import com.cappielloantonio.tempo.databinding.DialogDeleteDownloadStorageBinding;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.ExternalAudioReader;
|
||||
import com.cappielloantonio.tempo.util.ExternalDownloadMetadataStore;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
|
|
@ -47,7 +48,10 @@ public class DeleteDownloadStorageDialog extends DialogFragment {
|
|||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(v -> {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||
if (Preferences.getDownloadDirectoryUri() == null) {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||
}
|
||||
|
||||
String uriString = Preferences.getDownloadDirectoryUri();
|
||||
if (uriString != null) {
|
||||
DocumentFile directory = DocumentFile.fromTreeUri(requireContext(), Uri.parse(uriString));
|
||||
|
|
@ -57,6 +61,7 @@ public class DeleteDownloadStorageDialog extends DialogFragment {
|
|||
}
|
||||
}
|
||||
ExternalAudioReader.refreshCache();
|
||||
ExternalDownloadMetadataStore.clear();
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import androidx.fragment.app.DialogFragment;
|
|||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.util.ExternalAudioReader;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
|
||||
public class DownloadDirectoryPickerDialog extends DialogFragment {
|
||||
|
|
@ -37,6 +38,7 @@ public class DownloadDirectoryPickerDialog extends DialogFragment {
|
|||
);
|
||||
|
||||
Preferences.setDownloadDirectoryUri(uri.toString());
|
||||
ExternalAudioReader.refreshCache();
|
||||
|
||||
Toast.makeText(requireContext(), "Download directory set:\n" + uri.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,10 +138,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||
songs.stream().map(Download::new).collect(Collectors.toList())
|
||||
);
|
||||
} else {
|
||||
MappingUtil.mapMediaItems(songs).forEach(media -> {
|
||||
String title = media.mediaMetadata.title != null ? media.mediaMetadata.title.toString() : media.mediaId;
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), media, title);
|
||||
});
|
||||
songs.forEach(child -> ExternalAudioWriter.downloadToUserDirectory(requireContext(), child));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -117,10 +117,7 @@ public class DirectoryFragment extends Fragment implements ClickCallback {
|
|||
songs.stream().map(Download::new).collect(Collectors.toList())
|
||||
);
|
||||
} else {
|
||||
MappingUtil.mapMediaItems(songs).forEach(media -> {
|
||||
String title = media.mediaMetadata.title != null ? media.mediaMetadata.title.toString() : media.mediaId;
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), media, title);
|
||||
});
|
||||
songs.forEach(child -> ExternalAudioWriter.downloadToUserDirectory(requireContext(), child));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import com.cappielloantonio.tempo.subsonic.models.Child;
|
|||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.DownloadHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.ExternalAudioReader;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.DownloadViewModel;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
|
|
@ -289,6 +290,7 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
);
|
||||
Preferences.setDownloadDirectoryUri(uri.toString());
|
||||
ExternalAudioReader.refreshCache();
|
||||
Toast.makeText(requireContext(), "Download directory set", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,9 +123,7 @@ public class PlayerCoverFragment extends Fragment {
|
|||
new Download(song)
|
||||
);
|
||||
} else {
|
||||
MediaItem item = MappingUtil.mapMediaItem(song);
|
||||
String title = item.mediaMetadata.title != null ? item.mediaMetadata.title.toString() : item.mediaId;
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), item, title);
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), song);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -144,19 +144,16 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||
if (isVisible() && getActivity() != null) {
|
||||
if (Preferences.getDownloadDirectoryUri() == null) {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).download(
|
||||
MappingUtil.mapDownloads(songs),
|
||||
songs.stream().map(child -> {
|
||||
Download toDownload = new Download(child);
|
||||
toDownload.setPlaylistId(playlistPageViewModel.getPlaylist().getId());
|
||||
toDownload.setPlaylistName(playlistPageViewModel.getPlaylist().getName());
|
||||
return toDownload;
|
||||
}).collect(Collectors.toList())
|
||||
MappingUtil.mapDownloads(songs),
|
||||
songs.stream().map(child -> {
|
||||
Download toDownload = new Download(child);
|
||||
toDownload.setPlaylistId(playlistPageViewModel.getPlaylist().getId());
|
||||
toDownload.setPlaylistName(playlistPageViewModel.getPlaylist().getName());
|
||||
return toDownload;
|
||||
}).collect(Collectors.toList())
|
||||
);
|
||||
} else {
|
||||
MappingUtil.mapMediaItems(songs).forEach(media -> {
|
||||
String title = media.mediaMetadata.title != null ? media.mediaMetadata.title.toString() : media.mediaId;
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), media, title);
|
||||
});
|
||||
songs.forEach(child -> ExternalAudioWriter.downloadToUserDirectory(requireContext(), child));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||
if (current != null) {
|
||||
Preferences.setDownloadDirectoryUri(null);
|
||||
Preferences.setDownloadStoragePreference(0);
|
||||
ExternalAudioReader.refreshCache();
|
||||
Toast.makeText(requireContext(), "Download folder cleared.", Toast.LENGTH_SHORT).show();
|
||||
checkStorage();
|
||||
checkDownloadDirectory();
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.cappielloantonio.tempo.util.MappingUtil;
|
|||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.ExternalAudioWriter;
|
||||
import com.cappielloantonio.tempo.util.ExternalAudioReader;
|
||||
import com.cappielloantonio.tempo.viewmodel.AlbumBottomSheetViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
|
@ -167,10 +168,7 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||
if (Preferences.getDownloadDirectoryUri() == null) {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).download(mediaItems, downloads);
|
||||
} else {
|
||||
MappingUtil.mapMediaItems(songs).forEach(media -> {
|
||||
String title = media.mediaMetadata.title != null ? media.mediaMetadata.title.toString() : media.mediaId;
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), media, title);
|
||||
});
|
||||
songs.forEach(child -> ExternalAudioWriter.downloadToUserDirectory(requireContext(), child));
|
||||
}
|
||||
dismissBottomSheet();
|
||||
});
|
||||
|
|
@ -196,7 +194,11 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||
List<Download> downloads = songs.stream().map(Download::new).collect(Collectors.toList());
|
||||
|
||||
removeAll.setOnClickListener(v -> {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).remove(mediaItems, downloads);
|
||||
if (Preferences.getDownloadDirectoryUri() == null) {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).remove(mediaItems, downloads);
|
||||
} else {
|
||||
songs.forEach(ExternalAudioReader::delete);
|
||||
}
|
||||
dismissBottomSheet();
|
||||
});
|
||||
});
|
||||
|
|
@ -246,8 +248,15 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||
albumBottomSheetViewModel.getAlbumTracks().observe(getViewLifecycleOwner(), songs -> {
|
||||
List<MediaItem> mediaItems = MappingUtil.mapDownloads(songs);
|
||||
|
||||
if (Preferences.getDownloadDirectoryUri() == null && DownloadUtil.getDownloadTracker(requireContext()).areDownloaded(mediaItems)) {
|
||||
removeAll.setVisibility(View.VISIBLE);
|
||||
if (Preferences.getDownloadDirectoryUri() == null) {
|
||||
if (DownloadUtil.getDownloadTracker(requireContext()).areDownloaded(mediaItems)) {
|
||||
removeAll.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
removeAll.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
boolean hasLocal = songs.stream().anyMatch(song -> ExternalAudioReader.getUri(song) != null);
|
||||
removeAll.setVisibility(hasLocal ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,15 +164,13 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||
|
||||
TextView download = view.findViewById(R.id.download_text_view);
|
||||
download.setOnClickListener(v -> {
|
||||
MediaItem item = MappingUtil.mapMediaItem(song);
|
||||
String title = item.mediaMetadata.title != null ? item.mediaMetadata.title.toString() : item.mediaId;
|
||||
if (Preferences.getDownloadDirectoryUri() == null) {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).download(
|
||||
MappingUtil.mapDownload(song),
|
||||
new Download(song)
|
||||
);
|
||||
} else {
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), item, title);
|
||||
ExternalAudioWriter.downloadToUserDirectory(requireContext(), song);
|
||||
}
|
||||
dismissBottomSheet();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue