From 477331da6f0031fac9a542ee4fa8fc9fcf8206ed Mon Sep 17 00:00:00 2001 From: CappielloAntonio Date: Sun, 26 May 2024 14:49:57 +0200 Subject: [PATCH] feat: added external memory cache option --- .../dialog/StreamingCacheStorageDialog.java | 76 +++++++++++++++++++ .../tempo/ui/fragment/SettingsFragment.java | 42 +++++++++- .../tempo/util/DownloadUtil.java | 25 +++++- .../tempo/util/Preferences.kt | 14 ++++ .../layout/dialog_streaming_cache_storage.xml | 23 ++++++ app/src/main/res/values/strings.xml | 8 +- app/src/main/res/xml/global_preferences.xml | 4 + 7 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/cappielloantonio/tempo/ui/dialog/StreamingCacheStorageDialog.java create mode 100644 app/src/main/res/layout/dialog_streaming_cache_storage.xml diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/StreamingCacheStorageDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/StreamingCacheStorageDialog.java new file mode 100644 index 00000000..964da010 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/StreamingCacheStorageDialog.java @@ -0,0 +1,76 @@ +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.OptIn; +import androidx.fragment.app.DialogFragment; +import androidx.media3.common.util.UnstableApi; + +import com.cappielloantonio.tempo.R; +import com.cappielloantonio.tempo.databinding.DialogStreamingCacheStorageBinding; +import com.cappielloantonio.tempo.interfaces.DialogClickCallback; +import com.cappielloantonio.tempo.util.Preferences; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +@OptIn(markerClass = UnstableApi.class) +public class StreamingCacheStorageDialog extends DialogFragment { + private final DialogClickCallback dialogClickCallback; + + public StreamingCacheStorageDialog(DialogClickCallback dialogClickCallback) { + this.dialogClickCallback = dialogClickCallback; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + DialogStreamingCacheStorageBinding bind = DialogStreamingCacheStorageBinding.inflate(getLayoutInflater()); + + return new MaterialAlertDialogBuilder(getActivity()) + .setView(bind.getRoot()) + .setTitle(R.string.streaming_cache_storage_dialog_title) + .setPositiveButton(R.string.streaming_cache_storage_external_dialog_positive_button, null) + .setNegativeButton(R.string.streaming_cache_storage_internal_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 -> { + int currentPreference = Preferences.getStreamingCacheStoragePreference(); + int newPreference = 1; + + if (currentPreference != newPreference) { + Preferences.setStreamingCacheStoragePreference(newPreference); + dialogClickCallback.onPositiveClick(); + } + + dialog.dismiss(); + }); + + Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE); + negativeButton.setOnClickListener(v -> { + int currentPreference = Preferences.getStreamingCacheStoragePreference(); + int newPreference = 0; + + if (currentPreference != newPreference) { + Preferences.setStreamingCacheStoragePreference(newPreference); + dialogClickCallback.onNegativeClick(); + } + + dialog.dismiss(); + }); + } + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/SettingsFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/SettingsFragment.java index 56297dda..a02aaa65 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/SettingsFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/SettingsFragment.java @@ -3,6 +3,7 @@ package com.cappielloantonio.tempo.ui.fragment; import android.content.Intent; import android.media.audiofx.AudioEffect; import android.os.Bundle; +import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -30,8 +31,8 @@ import com.cappielloantonio.tempo.ui.activity.MainActivity; import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog; import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog; import com.cappielloantonio.tempo.ui.dialog.StarredSyncDialog; +import com.cappielloantonio.tempo.ui.dialog.StreamingCacheStorageDialog; import com.cappielloantonio.tempo.util.DownloadUtil; -import com.cappielloantonio.tempo.util.MusicUtil; import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.util.UIUtil; import com.cappielloantonio.tempo.viewmodel.SettingViewModel; @@ -84,6 +85,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { super.onResume(); checkEqualizer(); + checkCacheStorage(); checkStorage(); setStreamingCacheSize(); @@ -93,6 +95,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { actionLogout(); actionScan(); actionSyncStarredTracks(); + actionChangeStreamingCacheStorage(); actionChangeDownloadStorage(); actionDeleteDownloadStorage(); actionKeepScreenOn(); @@ -135,6 +138,22 @@ public class SettingsFragment extends PreferenceFragmentCompat { } } + private void checkCacheStorage() { + Preference storage = findPreference("streaming_cache_storage"); + + if (storage == null) return; + + try { + if (requireContext().getExternalFilesDirs(null)[1] == null) { + storage.setVisible(false); + } else { + storage.setSummary(Preferences.getDownloadStoragePreference() == 0 ? R.string.download_storage_internal_dialog_negative_button : R.string.download_storage_external_dialog_positive_button); + } + } catch (Exception exception) { + storage.setVisible(false); + } + } + private void checkStorage() { Preference storage = findPreference("download_storage"); @@ -213,7 +232,8 @@ public class SettingsFragment extends PreferenceFragmentCompat { @Override public void onSuccess(boolean isScanning, long count) { - getScanStatus(); + findPreference("scan_library").setSummary("Scanning: counting " + count + " tracks"); + if (isScanning) getScanStatus(); } }); @@ -233,6 +253,24 @@ public class SettingsFragment extends PreferenceFragmentCompat { }); } + private void actionChangeStreamingCacheStorage() { + findPreference("streaming_cache_storage").setOnPreferenceClickListener(preference -> { + StreamingCacheStorageDialog dialog = new StreamingCacheStorageDialog(new DialogClickCallback() { + @Override + public void onPositiveClick() { + findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_external_dialog_positive_button); + } + + @Override + public void onNegativeClick() { + findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_internal_dialog_negative_button); + } + }); + dialog.show(activity.getSupportFragmentManager(), null); + return true; + }); + } + private void actionChangeDownloadStorage() { findPreference("download_storage").setOnPreferenceClickListener(preference -> { DownloadStorageDialog dialog = new DownloadStorageDialog(new DialogClickCallback() { diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java b/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java index 93506b78..0a5e982d 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java +++ b/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java @@ -38,11 +38,13 @@ public final class DownloadUtil { public static final String DOWNLOAD_NOTIFICATION_SUCCESSFUL_GROUP = "com.cappielloantonio.tempo.SuccessfulDownload"; public static final String DOWNLOAD_NOTIFICATION_FAILED_GROUP = "com.cappielloantonio.tempo.FailedDownload"; + private static final String STREAMING_CACHE_CONTENT_DIRECTORY = "streaming_cache"; private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads"; private static DataSource.Factory dataSourceFactory; private static DataSource.Factory httpDataSourceFactory; private static DatabaseProvider databaseProvider; + private static File streamingCacheDirectory; private static File downloadDirectory; private static Cache downloadCache; private static SimpleCache streamingCache; @@ -135,7 +137,7 @@ public final class DownloadUtil { private static synchronized SimpleCache getStreamingCache(Context context) { if (streamingCache == null) { - File streamingCacheDirectory = new File(context.getCacheDir(), "streamingCache"); + File streamingCacheDirectory = new File(getStreamingCacheDirectory(context), STREAMING_CACHE_CONTENT_DIRECTORY); streamingCache = new SimpleCache( streamingCacheDirectory, @@ -169,6 +171,27 @@ public final class DownloadUtil { return databaseProvider; } + private static synchronized File getStreamingCacheDirectory(Context context) { + if (streamingCacheDirectory == null) { + if (Preferences.getStreamingCacheStoragePreference() == 0) { + streamingCacheDirectory = context.getExternalFilesDirs(null)[0]; + if (streamingCacheDirectory == null) { + streamingCacheDirectory = context.getFilesDir(); + } + } else { + try { + streamingCacheDirectory = context.getExternalFilesDirs(null)[1]; + } catch (Exception exception) { + streamingCacheDirectory = context.getExternalFilesDirs(null)[0]; + Preferences.setStreamingCacheStoragePreference(0); + } + + } + } + + return streamingCacheDirectory; + } + private static synchronized File getDownloadDirectory(Context context) { if (downloadDirectory == null) { if (Preferences.getDownloadStoragePreference() == 0) { diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt index 66d948ec..3abfd13a 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt @@ -42,6 +42,7 @@ object Preferences { private const val MUSIC_DIRECTORY_SECTION_VISIBILITY = "music_directory_section_visibility" private const val REPLAY_GAIN_MODE = "replay_gain_mode" private const val AUDIO_TRANSCODE_PRIORITY = "audio_transcode_priority" + private const val STREAMING_CACHE_STORAGE = "streaming_cache_storage" private const val DOWNLOAD_STORAGE = "download_storage" private const val DEFAULT_DOWNLOAD_VIEW_TYPE = "default_download_view_type" private const val AUDIO_TRANSCODE_DOWNLOAD = "audio_transcode_download" @@ -310,6 +311,19 @@ object Preferences { return App.getInstance().preferences.getBoolean(AUDIO_TRANSCODE_PRIORITY, false) } + @JvmStatic + fun getStreamingCacheStoragePreference(): Int { + return App.getInstance().preferences.getString(STREAMING_CACHE_STORAGE, "0")!!.toInt() + } + + @JvmStatic + fun setStreamingCacheStoragePreference(streamingCachePreference: Int) { + return App.getInstance().preferences.edit().putString( + STREAMING_CACHE_STORAGE, + streamingCachePreference.toString() + ).apply() + } + @JvmStatic fun getDownloadStoragePreference(): Int { return App.getInstance().preferences.getString(DOWNLOAD_STORAGE, "0")!!.toInt() diff --git a/app/src/main/res/layout/dialog_streaming_cache_storage.xml b/app/src/main/res/layout/dialog_streaming_cache_storage.xml new file mode 100644 index 00000000..8ae48b29 --- /dev/null +++ b/app/src/main/res/layout/dialog_streaming_cache_storage.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index da1f2ff5..9bbafd15 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -289,8 +289,9 @@ Scan library Enable music scrobbling Enable music sharing - It\'s important to note that scrobbling also relies on the server being enabled to receive this data. Size of streaming cache + Streaming cache storage + It\'s important to note that scrobbling also relies on the server being enabled to receive this data. When listening to an artist\'s radio, an instant mix or when shuffling all, tracks below a certain user rating will be ignored. Replay gain is a feature that allows you to adjust the volume level of audio tracks for a consistent listening experience. This setting is only effective if the track contains the necessary metadata. Scrobbling is a feature that allows your device to send information about the songs you listen to the music server. This information helps create personalized recommendations based on your music preferences. @@ -355,6 +356,11 @@ Continue and download Downloading starry tracks may require a large amount of data. Sync starred tracks + For the changes to take effect, restart the app. + Changing the destination of cached files from one storage to another may result in the deletion of any previously cached files in the other storage. + Select storage option + External + Internal Album Artist Bitrate diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml index 1dd0f660..55b4f525 100644 --- a/app/src/main/res/xml/global_preferences.xml +++ b/app/src/main/res/xml/global_preferences.xml @@ -137,6 +137,10 @@ app:summary="@string/settings_buffering_strategy_summary" app:useSimpleSummaryProvider="false" /> + +