diff --git a/app/build.gradle b/app/build.gradle index 8a7ac3615..ba5a1f566 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -57,6 +57,8 @@ android { abortOnError false } compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -149,6 +151,8 @@ dependencies { kapt 'com.github.bumptech.glide:compiler:4.12.0' implementation 'com.github.bumptech.glide:okhttp3-integration:4.12.0' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' + implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0' diff --git a/app/src/debug/res/values/donottranslate.xml b/app/src/debug/res/values/donottranslate.xml index 7d3f0ee62..a67ed0a23 100644 --- a/app/src/debug/res/values/donottranslate.xml +++ b/app/src/debug/res/values/donottranslate.xml @@ -1,5 +1,7 @@ + Retro Music-Debug + true false \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt index a8e96d211..bab9ec6d6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.kt +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt @@ -135,6 +135,7 @@ const val LOCK_SCREEN = "lock_screen" const val ALBUM_ARTISTS_ONLY = "album_artists_only" const val ALBUM_ARTIST = "album_artist" const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order" +const val ARTIST_DETAIL_SONG_SORT_ORDER = "artist_detail_song_sort_order" const val LYRICS_OPTIONS = "lyrics_tab_position" const val CHOOSE_EQUALIZER = "choose_equalizer" const val EQUALIZER = "equalizer" diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt index 716fef95c..e9aa2a3b3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt @@ -62,7 +62,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener { if (!hasPermissions()) { findNavController(R.id.fragment_container).navigate(R.id.permissionFragment) } - if (BuildConfig.VERSION_CODE > PreferenceUtil.lastVersion){ + if (BuildConfig.VERSION_CODE > PreferenceUtil.lastVersion && !BuildConfig.DEBUG){ NavigationUtil.gotoWhatNews(this) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java deleted file mode 100644 index 2354bb9af..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2019 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by - * the Free Software Foundation either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - */ - -package code.name.monkey.retromusic.adapter; - -import android.annotation.SuppressLint; -import android.content.res.ColorStateList; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.RecyclerView; - -import com.google.android.material.checkbox.MaterialCheckBox; - -import java.util.List; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.model.CategoryInfo; -import code.name.monkey.retromusic.util.SwipeAndDragHelper; - -public class CategoryInfoAdapter extends RecyclerView.Adapter - implements SwipeAndDragHelper.ActionCompletionContract { - - private List categoryInfos; - private final ItemTouchHelper touchHelper; - - public CategoryInfoAdapter() { - SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this); - touchHelper = new ItemTouchHelper(swipeAndDragHelper); - } - - public void attachToRecyclerView(RecyclerView recyclerView) { - touchHelper.attachToRecyclerView(recyclerView); - } - - @NonNull - public List getCategoryInfos() { - return categoryInfos; - } - - public void setCategoryInfos(@NonNull List categoryInfos) { - this.categoryInfos = categoryInfos; - notifyDataSetChanged(); - } - - @Override - public int getItemCount() { - return categoryInfos.size(); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) { - CategoryInfo categoryInfo = categoryInfos.get(position); - - holder.checkBox.setChecked(categoryInfo.isVisible()); - holder.title.setText( - holder.title.getResources().getString(categoryInfo.getCategory().getStringRes())); - - holder.itemView.setOnClickListener( - v -> { - if (!(categoryInfo.isVisible() && isLastCheckedCategory(categoryInfo))) { - categoryInfo.setVisible(!categoryInfo.isVisible()); - holder.checkBox.setChecked(categoryInfo.isVisible()); - } else { - Toast.makeText( - holder.itemView.getContext(), - R.string.you_have_to_select_at_least_one_category, - Toast.LENGTH_SHORT) - .show(); - } - }); - - holder.dragView.setOnTouchListener( - (view, event) -> { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - touchHelper.startDrag(holder); - } - return false; - }); - } - - @Override - @NonNull - public CategoryInfoAdapter.ViewHolder onCreateViewHolder( - @NonNull ViewGroup parent, int viewType) { - View view = - LayoutInflater.from(parent.getContext()) - .inflate(R.layout.preference_dialog_library_categories_listitem, parent, false); - return new ViewHolder(view); - } - - @Override - public void onViewMoved(int oldPosition, int newPosition) { - CategoryInfo categoryInfo = categoryInfos.get(oldPosition); - categoryInfos.remove(oldPosition); - categoryInfos.add(newPosition, categoryInfo); - notifyItemMoved(oldPosition, newPosition); - } - - private boolean isLastCheckedCategory(CategoryInfo categoryInfo) { - if (categoryInfo.isVisible()) { - for (CategoryInfo c : categoryInfos) { - if (c != categoryInfo && c.isVisible()) { - return false; - } - } - } - return true; - } - - static class ViewHolder extends RecyclerView.ViewHolder { - private final MaterialCheckBox checkBox; - private final View dragView; - private final TextView title; - - ViewHolder(View view) { - super(view); - checkBox = view.findViewById(R.id.checkbox); - checkBox.setButtonTintList( - ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext()))); - title = view.findViewById(R.id.title); - dragView = view.findViewById(R.id.drag_view); - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.kt new file mode 100644 index 000000000..0347d42b7 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.kt @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Hemanth Savarala. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ +package code.name.monkey.retromusic.adapter + +import android.annotation.SuppressLint +import android.content.res.ColorStateList +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.databinding.PreferenceDialogLibraryCategoriesListitemBinding +import code.name.monkey.retromusic.model.CategoryInfo +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.SwipeAndDragHelper +import code.name.monkey.retromusic.util.SwipeAndDragHelper.ActionCompletionContract + +class CategoryInfoAdapter : RecyclerView.Adapter(), + ActionCompletionContract { + var categoryInfos: MutableList = + PreferenceUtil.libraryCategory.toMutableList() + @SuppressLint("NotifyDataSetChanged") + set(value) { + field = value + notifyDataSetChanged() + } + private val touchHelper: ItemTouchHelper + fun attachToRecyclerView(recyclerView: RecyclerView?) { + touchHelper.attachToRecyclerView(recyclerView) + } + + override fun getItemCount(): Int { + return categoryInfos.size + } + + @SuppressLint("ClickableViewAccessibility") + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val categoryInfo = categoryInfos[position] + holder.binding.checkbox.isChecked = categoryInfo.visible + holder.binding.title.text = + holder.binding.title.resources.getString(categoryInfo.category.stringRes) + holder.itemView.setOnClickListener { + if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) { + categoryInfo.visible = !categoryInfo.visible + holder.binding.checkbox.isChecked = categoryInfo.visible + } else { + Toast.makeText( + holder.itemView.context, + R.string.you_have_to_select_at_least_one_category, + Toast.LENGTH_SHORT + ) + .show() + } + } + holder.binding.dragView.setOnTouchListener { _: View?, event: MotionEvent -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + touchHelper.startDrag(holder) + } + false + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, viewType: Int + ): ViewHolder { + return ViewHolder( + PreferenceDialogLibraryCategoriesListitemBinding.inflate( + LayoutInflater.from( + parent.context + ), parent, false + ) + ) + } + + override fun onViewMoved(oldPosition: Int, newPosition: Int) { + val categoryInfo = categoryInfos[oldPosition] + categoryInfos.removeAt(oldPosition) + categoryInfos.add(newPosition, categoryInfo) + notifyItemMoved(oldPosition, newPosition) + } + + private fun isLastCheckedCategory(categoryInfo: CategoryInfo): Boolean { + if (categoryInfo.visible) { + for (c in categoryInfos) { + if (c !== categoryInfo && c.visible) { + return false + } + } + } + return true + } + + class ViewHolder(val binding: PreferenceDialogLibraryCategoriesListitemBinding) : + RecyclerView.ViewHolder(binding.root) { + + init { + binding.checkbox.buttonTintList = + ColorStateList.valueOf(accentColor(binding.checkbox.context)) + } + } + + init { + val swipeAndDragHelper = SwipeAndDragHelper(this) + touchHelper = ItemTouchHelper(swipeAndDragHelper) + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt index ea4e6e25e..383c19e26 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt @@ -14,6 +14,7 @@ */ package code.name.monkey.retromusic.adapter +import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -21,7 +22,7 @@ import android.view.ViewOutlineProvider import androidx.fragment.app.FragmentActivity import androidx.recyclerview.widget.RecyclerView import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.databinding.ItemGenreBinding import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroMusicColoredTarget @@ -38,7 +39,6 @@ import java.util.* class GenreAdapter( private val activity: FragmentActivity, var dataSet: List, - private val mItemLayoutRes: Int, private val listener: IGenreClickListener ) : RecyclerView.Adapter() { @@ -51,13 +51,13 @@ class GenreAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false)) + return ViewHolder(ItemGenreBinding.inflate(LayoutInflater.from(activity), parent, false)) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val genre = dataSet[position] - holder.title?.text = genre.name - holder.text?.text = String.format( + holder.binding.title.text = genre.name + holder.binding.text.text = String.format( Locale.getDefault(), "%d %s", genre.songCount, @@ -72,33 +72,39 @@ class GenreAdapter( .asBitmapPalette() .load(RetroGlideExtension.getSongModel(genreSong)) .songCoverOptions(genreSong) - .into(object : RetroMusicColoredTarget(holder.image!!) { + .into(object : RetroMusicColoredTarget(holder.binding.image) { override fun onColorReady(colors: MediaNotificationProcessor) { setColors(holder, colors) } }) // Just for a bit of shadow around image - holder.image?.outlineProvider = ViewOutlineProvider.BOUNDS + holder.binding.image.outlineProvider = ViewOutlineProvider.BOUNDS } private fun setColors(holder: ViewHolder, color: MediaNotificationProcessor) { - holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor) - holder.title?.setTextColor(color.primaryTextColor) - holder.text?.setTextColor(color.secondaryTextColor) + holder.binding.imageContainerCard.setCardBackgroundColor(color.backgroundColor) + holder.binding.title.setTextColor(color.primaryTextColor) + holder.binding.text.setTextColor(color.secondaryTextColor) } override fun getItemCount(): Int { return dataSet.size } + @SuppressLint("NotifyDataSetChanged") fun swapDataSet(list: List) { dataSet = list notifyDataSetChanged() } - inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { + inner class ViewHolder(val binding: ItemGenreBinding) : RecyclerView.ViewHolder(binding.root), + View.OnClickListener { override fun onClick(v: View?) { listener.onClickGenre(dataSet[layoutPosition], itemView) } + + init { + itemView.setOnClickListener(this) + } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt index 3e84aee43..35a2b2f88 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt @@ -248,7 +248,6 @@ class HomeAdapter( val genreAdapter = GenreAdapter( activity, home.arrayList as List, - R.layout.item_grid_genre, this@HomeAdapter ) recyclerView.apply { diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt index 81b2bbf83..adfcecf1b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt @@ -1,15 +1,14 @@ package code.name.monkey.retromusic.adapter.backup +import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.MenuItem -import android.view.View import android.view.ViewGroup -import android.widget.TextView -import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.PopupMenu import androidx.fragment.app.FragmentActivity import androidx.recyclerview.widget.RecyclerView import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.databinding.ItemListBackupBinding import java.io.File @@ -21,27 +20,27 @@ class BackupAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder( - LayoutInflater.from(activity).inflate(R.layout.item_list_card, parent, false) + ItemListBackupBinding.inflate(LayoutInflater.from(activity), parent, false) ) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.title.text = dataSet[position].nameWithoutExtension + holder.binding.title.text = dataSet[position].nameWithoutExtension } override fun getItemCount(): Int = dataSet.size + @SuppressLint("NotifyDataSetChanged") fun swapDataset(dataSet: List) { this.dataSet = ArrayList(dataSet) notifyDataSetChanged() } - inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - val title: TextView = itemView.findViewById(R.id.title) - val menu: AppCompatImageView = itemView.findViewById(R.id.menu) + inner class ViewHolder(val binding: ItemListBackupBinding) : + RecyclerView.ViewHolder(binding.root) { init { - menu.setOnClickListener { view -> + binding.menu.setOnClickListener { view -> val popupMenu = PopupMenu(activity, view) popupMenu.inflate(R.menu.menu_backup) popupMenu.setOnMenuItemClickListener { menuItem -> diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt index e7b771054..bc8d72cc9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt @@ -255,11 +255,13 @@ class LibraryViewModel( } repository.insertSongs(songEntities) } else { - val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name)) - val songEntities = playlist.getSongs().map { - it.toSongEntity(playListId) + if (playlist != Playlist.empty){ + val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name)) + val songEntities = playlist.getSongs().map { + it.toSongEntity(playListId) + } + repository.insertSongs(songEntities) } - repository.insertSongs(songEntities) } forceReload(Playlists) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt index 39c092e69..750e79133 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt @@ -5,11 +5,9 @@ import android.content.Intent import android.graphics.Color import android.os.Bundle import android.text.Spanned -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View +import android.view.* import androidx.activity.addCallback +import androidx.appcompat.widget.PopupMenu import androidx.core.os.bundleOf import androidx.core.text.HtmlCompat import androidx.core.view.ViewCompat @@ -32,6 +30,7 @@ import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.interfaces.IAlbumClickListener import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder @@ -39,10 +38,7 @@ import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.repository.RealRepository -import code.name.monkey.retromusic.util.CustomArtistImageUtil -import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.RetroColorUtil -import code.name.monkey.retromusic.util.RetroUtil +import code.name.monkey.retromusic.util.* import com.afollestad.materialcab.attached.AttachedCab import com.afollestad.materialcab.attached.destroy import com.afollestad.materialcab.attached.isActive @@ -71,6 +67,9 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm private var lang: String? = null private var biography: Spanned? = null + private val savedSongSortOrder: String + get() = PreferenceUtil.artistDetailSongSortOrder + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = MaterialContainerTransform().apply { @@ -101,7 +100,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm setupRecyclerView() binding.fragmentArtistContent.playAction.apply { - setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } + setOnClickListener { MusicPlayerRemote.openQueue(artist.sortedSongs, 0, true) } } binding.fragmentArtistContent.shuffleAction.apply { setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) } @@ -121,6 +120,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm requireActivity().onBackPressed() } } + setupSongSortButton() binding.appBarLayout?.statusBarForeground = MaterialShapeDrawable.createWithElevationOverlay(requireContext()) } @@ -168,7 +168,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm ) binding.fragmentArtistContent.songTitle.text = songText binding.fragmentArtistContent.albumTitle.text = albumText - songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber }) + songAdapter.swapDataSet(artist.sortedSongs) albumAdapter.swapDataSet(artist.albums) } @@ -289,6 +289,53 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm return true } + private fun setupSongSortButton() { + binding.fragmentArtistContent.songSortOrder.setOnClickListener { + PopupMenu(requireContext(), binding.fragmentArtistContent.songSortOrder).apply { + inflate(R.menu.menu_artist_song_sort_order) + setUpSortOrderMenu(menu) + setOnMenuItemClickListener { menuItem -> + val sortOrder = when (menuItem.itemId) { + R.id.action_sort_order_title -> SortOrder.ArtistSongSortOrder.SONG_A_Z + R.id.action_sort_order_title_desc -> SortOrder.ArtistSongSortOrder.SONG_Z_A + R.id.action_sort_order_album -> SortOrder.ArtistSongSortOrder.SONG_ALBUM + R.id.action_sort_order_year -> SortOrder.ArtistSongSortOrder.SONG_YEAR + R.id.action_sort_order_song_duration -> SortOrder.ArtistSongSortOrder.SONG_DURATION + else -> { + throw IllegalArgumentException("invalid ${menuItem.title}") + } + } + menuItem.isChecked = true + setSaveSortOrder(sortOrder) + return@setOnMenuItemClickListener true + } + show() + } + } + } + + private fun setSaveSortOrder(sortOrder: String) { + PreferenceUtil.artistDetailSongSortOrder = sortOrder + songAdapter.swapDataSet(artist.sortedSongs) + } + + private fun setUpSortOrderMenu(sortOrder: Menu) { + when (savedSongSortOrder) { + SortOrder.ArtistSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title).isChecked = true + SortOrder.ArtistSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc).isChecked = true + SortOrder.ArtistSongSortOrder.SONG_ALBUM -> + sortOrder.findItem(R.id.action_sort_order_album).isChecked = true + SortOrder.ArtistSongSortOrder.SONG_YEAR -> + sortOrder.findItem(R.id.action_sort_order_year).isChecked = true + SortOrder.ArtistSongSortOrder.SONG_DURATION -> + sortOrder.findItem(R.id.action_sort_order_song_duration).isChecked = true + else-> { + throw IllegalArgumentException("invalid $savedSongSortOrder") + } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt index 170ae9387..efa11bcd6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt @@ -17,6 +17,8 @@ import androidx.recyclerview.widget.RecyclerView import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.backup.BackupAdapter import code.name.monkey.retromusic.databinding.FragmentBackupBinding +import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.retromusic.extensions.accentOutlineColor import code.name.monkey.retromusic.helper.BackupHelper import code.name.monkey.retromusic.helper.sanitize import code.name.monkey.retromusic.util.BackupUtil @@ -45,7 +47,7 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC else backupAdapter?.swapDataset(listOf()) } - backupViewModel.loadBackups() + backupViewModel.loadBackups(requireContext()) val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) { lifecycleScope.launch(Dispatchers.IO) { it?.let { @@ -55,6 +57,8 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC } } } + binding.createBackup.accentOutlineColor() + binding.restoreBackup.accentColor() binding.createBackup.setOnClickListener { showCreateBackupDialog() } @@ -91,11 +95,11 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC MaterialDialog(requireContext()).show { cornerRadius(res = R.dimen.m3_card_corner_radius) title(res = R.string.action_rename) - input(prefill = System.currentTimeMillis().toString()) { _, text -> + input(prefill = BackupHelper.getTimeStamp()) { _, text -> // Text submitted with the action button lifecycleScope.launch { BackupHelper.createBackup(requireContext(), text.sanitize()) - backupViewModel.loadBackups() + backupViewModel.loadBackups(requireContext()) } } positiveButton(android.R.string.ok) @@ -125,7 +129,7 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC Toast.LENGTH_SHORT ).show() } - backupViewModel.loadBackups() + backupViewModel.loadBackups(requireContext()) return true } R.id.action_share -> { @@ -143,10 +147,10 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC input(prefill = file.nameWithoutExtension) { _, text -> // Text submitted with the action button val renamedFile = - File(file.parent + File.separator + text + BackupHelper.APPEND_EXTENSION) + File(file.parent, "$text${BackupHelper.APPEND_EXTENSION}") if (!renamedFile.exists()) { file.renameTo(renamedFile) - backupViewModel.loadBackups() + backupViewModel.loadBackups(requireContext()) } else { Toast.makeText( requireContext(), diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupViewModel.kt index f482cc915..87e3f05dd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupViewModel.kt @@ -1,6 +1,7 @@ package code.name.monkey.retromusic.fragments.backup import android.app.Activity +import android.content.Context import android.content.Intent import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -19,8 +20,8 @@ class BackupViewModel : ViewModel() { private val backupsMutableLiveData = MutableLiveData>() val backupsLiveData: LiveData> = backupsMutableLiveData - fun loadBackups() { - File(BackupHelper.backupRootPath).listFiles { _, name -> + fun loadBackups(context: Context) { + BackupHelper.getBackupRoot(context).listFiles { _, name -> return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION) }?.toList()?.let { backupsMutableLiveData.value = it @@ -29,13 +30,16 @@ class BackupViewModel : ViewModel() { suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List) { BackupHelper.restoreBackup(activity, inputStream, contents) - withContext(Dispatchers.Main) { - val intent = Intent( - activity, - MainActivity::class.java - ) - activity.startActivity(intent) - exitProcess(0) + if (contents.contains(BackupContent.SETTINGS) or contents.contains(BackupContent.CUSTOM_ARTIST_IMAGES)) { + // We have to restart App when Preferences i.e. Settings or Artist Images are to be restored + withContext(Dispatchers.Main) { + val intent = Intent( + activity, + MainActivity::class.java + ) + activity.startActivity(intent) + exitProcess(0) + } } } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/RestoreActivity.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/RestoreActivity.kt index feab240ea..af92c73e2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/RestoreActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/RestoreActivity.kt @@ -3,9 +3,11 @@ package code.name.monkey.retromusic.fragments.backup import android.net.Uri import android.os.Bundle import android.provider.MediaStore +import android.view.ViewGroup import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate +import androidx.core.view.updateLayoutParams import androidx.lifecycle.lifecycleScope import code.name.monkey.retromusic.databinding.ActivityRestoreBinding import code.name.monkey.retromusic.helper.BackupContent @@ -27,6 +29,7 @@ class RestoreActivity : AppCompatActivity() { super.onCreate(savedInstanceState) binding = ActivityRestoreBinding.inflate(layoutInflater) setContentView(binding.root) + setWidth() val backupUri = intent?.data binding.backupName.setText(getFileName(backupUri)) binding.cancelButton.setOnClickListener { @@ -35,7 +38,6 @@ class RestoreActivity : AppCompatActivity() { binding.restoreButton.setOnClickListener { val backupContents = mutableListOf() if (binding.checkSettings.isChecked) backupContents.add(SETTINGS) - if (binding.checkQueue.isChecked) backupContents.add(QUEUE) if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS) if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES) if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES) @@ -67,17 +69,13 @@ class RestoreActivity : AppCompatActivity() { return uri.lastPathSegment } "content" -> { - val proj = arrayOf(MediaStore.Images.Media.TITLE) + val proj = arrayOf(MediaStore.Files.FileColumns.DISPLAY_NAME) contentResolver.query( - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { - MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL) - } else { - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI - }, proj, null, null, null + uri, proj, null, null, null )?.use { cursor -> if (cursor.count != 0) { val columnIndex: Int = - cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE) + cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME) cursor.moveToFirst() return cursor.getString(columnIndex) } @@ -86,4 +84,9 @@ class RestoreActivity : AppCompatActivity() { } return "Backup" } + + private fun setWidth() { + val width = resources.displayMetrics.widthPixels * 0.8 + binding.root.updateLayoutParams { this.width = width.toInt() } + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt index 06fac152b..979e3646a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt @@ -164,7 +164,7 @@ abstract class AbsRecyclerViewFragment, LM : Recycle if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) { binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height_expanded)) } else { - binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height)) + binding.recyclerView.updatePadding(bottom = dip(R.dimen.bottom_nav_height)) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt index 603717767..a1b6cdb63 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt @@ -62,7 +62,7 @@ GenresFragment : AbsRecyclerViewFragment(), override fun createAdapter(): GenreAdapter { val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet - return GenreAdapter(requireActivity(), dataSet, R.layout.item_genre, this) + return GenreAdapter(requireActivity(), dataSet, this) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/other/UserInfoFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/other/UserInfoFragment.kt index a1d749fd6..16cc7268f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/other/UserInfoFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/other/UserInfoFragment.kt @@ -29,6 +29,7 @@ import android.widget.Toast import androidx.core.view.doOnPreDraw import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_PROFILE @@ -50,7 +51,6 @@ import com.bumptech.glide.request.target.Target import com.github.dhaval2404.imagepicker.ImagePicker import com.github.dhaval2404.imagepicker.constant.ImageProvider import com.google.android.material.transition.MaterialContainerTransform -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -202,15 +202,15 @@ class UserInfoFragment : Fragment() { } private fun saveImage(bitmap: Bitmap, fileName: String) { - CoroutineScope(Dispatchers.IO).launch { + lifecycleScope.launch(Dispatchers.IO) { val appDir = requireContext().filesDir val file = File(appDir, fileName) var successful = false runCatching { - val os = BufferedOutputStream(FileOutputStream(file)) - successful = ImageUtil.resizeBitmap(bitmap, 2048) - .compress(Bitmap.CompressFormat.WEBP, 100, os) - withContext(Dispatchers.IO) { os.close() } + BufferedOutputStream(FileOutputStream(file)).use { + successful = ImageUtil.resizeBitmap(bitmap, 2048) + .compress(Bitmap.CompressFormat.WEBP, 100, it) + } }.onFailure { it.printStackTrace() } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt index 13fa1e28d..c3cce3120 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt @@ -14,12 +14,13 @@ */ package code.name.monkey.retromusic.fragments.player +import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.content.SharedPreferences import android.graphics.Color import android.os.Bundle import android.view.View -import androidx.core.view.isInvisible +import androidx.core.animation.doOnEnd import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager @@ -34,7 +35,6 @@ import code.name.monkey.retromusic.extensions.isColorLight import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment -import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.goToLyrics import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper @@ -223,8 +223,13 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe } private fun showLyrics(visible: Boolean) { - lrcView.isVisible = visible - viewPager.isInvisible = visible + ObjectAnimator.ofFloat(lrcView, View.ALPHA, if (visible) 1F else 0F).apply { + doOnEnd { + lrcView.isVisible = visible + } + start() + } + ObjectAnimator.ofFloat(viewPager, View.ALPHA, if (visible) 0F else 1F).start() } private fun maybeInitLyrics() { @@ -233,8 +238,6 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) { showLyrics(true) progressViewUpdateHelper?.start() - lrcView.animate().alpha(1f).duration = - AbsPlayerFragment.VISIBILITY_ANIM_DURATION } else { showLyrics(false) progressViewUpdateHelper?.stop() diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt index d37d475e2..c76d5b456 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt @@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.player.color import android.animation.ValueAnimator import android.os.Bundle +import android.os.Handler import android.view.View import androidx.appcompat.widget.Toolbar import androidx.core.animation.doOnEnd @@ -60,11 +61,13 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) { _binding?.root?.setBackgroundColor(color.backgroundColor) } animator.start() - ToolbarContentTintHelper.colorizeToolbar( - binding.playerToolbar, - color.secondaryTextColor, - requireActivity() - ) + Handler().post { + ToolbarContentTintHelper.colorizeToolbar( + binding.playerToolbar, + color.secondaryTextColor, + requireActivity() + ) + } } override fun onFavoriteToggled() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt index c9a2bc778..f9ca2ce38 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt @@ -28,9 +28,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.Toolbar import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding +import androidx.core.view.* import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -65,8 +63,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_player), - MusicProgressViewUpdateHelper.Callback, - View.OnLayoutChangeListener, PopupMenu.OnMenuItemClickListener { + MusicProgressViewUpdateHelper.Callback, PopupMenu.OnMenuItemClickListener { private var lastColor: Int = 0 private var lastPlaybackControlsColor: Int = 0 private var lastDisabledPlaybackControlsColor: Int = 0 @@ -89,8 +86,8 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play binding.playerQueueSheet.updatePadding( top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt() ) - binding.recyclerView.updatePadding( - top = ((1 - slideOffset) * navBarHeight).toInt() + binding.container.updatePadding( + bottom = ((1 - slideOffset) * navBarHeight).toInt() ) } @@ -129,9 +126,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play } private fun setupPanel() { - if (!ViewCompat.isLaidOut(binding.colorBackground) || binding.colorBackground.isLayoutRequested) { - binding.colorBackground.addOnLayoutChangeListener(this) - return + binding.colorBackground.doOnLayout { + val panel = getQueuePanel() + panel.peekHeight = binding.container.height } } @@ -158,9 +155,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play } ViewCompat.setOnApplyWindowInsetsListener( (binding.container) - ) { _: View, insets: WindowInsetsCompat -> + ) { v: View, insets: WindowInsetsCompat -> navBarHeight = insets.safeGetBottomInsets() - binding.recyclerView.updatePadding(top = navBarHeight) + v.updatePadding(bottom = navBarHeight) insets } binding.playbackControlsFragment.root.drawAboveSystemBars() @@ -461,21 +458,6 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play } } - override fun onLayoutChange( - v: View?, - left: Int, - top: Int, - right: Int, - bottom: Int, - oldLeft: Int, - oldTop: Int, - oldRight: Int, - oldBottom: Int - ) { - val panel = getQueuePanel() - panel.peekHeight = binding.container.height + navBarHeight - } - private fun setupRecyclerView() { playingQueueAdapter = PlayingQueueAdapter( requireActivity() as AppCompatActivity, diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt index 3b1e1cde2..4b786bb33 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt @@ -2,39 +2,49 @@ package code.name.monkey.retromusic.helper import android.content.Context import android.os.Environment -import android.util.Log import android.widget.Toast -import androidx.core.content.edit -import androidx.preference.PreferenceManager import code.name.monkey.retromusic.App import code.name.monkey.retromusic.BuildConfig +import code.name.monkey.retromusic.db.PlaylistEntity +import code.name.monkey.retromusic.db.toSongEntity import code.name.monkey.retromusic.helper.BackupContent.* +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.repository.Repository +import code.name.monkey.retromusic.repository.SongRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.io.* +import java.text.SimpleDateFormat +import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream -object BackupHelper { +object BackupHelper : KoinComponent { + private val repository by inject() + private val songRepository by inject() + suspend fun createBackup(context: Context, name: String) { val backupFile = - File(backupRootPath + File.separator + name + APPEND_EXTENSION) + File(getBackupRoot(context), name + APPEND_EXTENSION) if (backupFile.parentFile?.exists() != true) { backupFile.parentFile?.mkdirs() } val zipItems = mutableListOf() - zipItems.addAll(getDatabaseZipItems(context)) + zipItems.addAll(getPlaylistZipItems(context)) zipItems.addAll(getSettingsZipItems(context)) getUserImageZipItems(context)?.let { zipItems.addAll(it) } zipItems.addAll(getCustomArtistZipItems(context)) - zipItems.addAll(getQueueZipItems(context)) zipAll(zipItems, backupFile) + // Clean Cache Playlist Directory + File(context.filesDir, PLAYLISTS_PATH).deleteRecursively() } private suspend fun zipAll(zipItems: List, backupFile: File) = withContext(Dispatchers.IO) { - kotlin.runCatching { + runCatching { ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out -> for (zipItem in zipItems) { FileInputStream(zipItem.filePath).use { fi -> @@ -51,7 +61,6 @@ object BackupHelper { Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT) .show() } - throw Exception(it) }.onSuccess { withContext(Dispatchers.Main) { Toast.makeText( @@ -62,34 +71,39 @@ object BackupHelper { .show() } } - } - private fun getDatabaseZipItems(context: Context): List { - return context.databaseList().filter { - it.endsWith(".db") && it != queueDatabase - }.map { - ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it") + private suspend fun getPlaylistZipItems(context: Context): List { + val playlistZipItems = mutableListOf() + // Cache Playlist files in App storage + val playlistFolder = File(context.filesDir, PLAYLISTS_PATH) + if (!playlistFolder.exists()) { + playlistFolder.mkdirs() } - } - - private fun getQueueZipItems(context: Context): List { - Log.d("RetroMusic", context.getDatabasePath(queueDatabase).absolutePath) - return listOf( - ZipItem( - context.getDatabasePath(queueDatabase).absolutePath, - "$QUEUE_PATH${File.separator}$queueDatabase" - ) - ) + for (playlist in repository.fetchPlaylistWithSongs()) { + runCatching { + M3UWriter.writeIO(playlistFolder, playlist) + }.onSuccess { playlistFile -> + if (playlistFile.exists()) { + playlistZipItems.add( + ZipItem( + playlistFile.absolutePath, + PLAYLISTS_PATH.child(playlistFile.name) + ) + ) + } + } + } + return playlistZipItems } private fun getSettingsZipItems(context: Context): List { - val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + val sharedPrefPath = File(context.filesDir.parentFile, "shared_prefs") return listOf( "${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path "$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path ).map { - ZipItem(sharedPrefPath + it, "$SETTINGS_PATH${File.separator}$it") + ZipItem(File(sharedPrefPath, it).absolutePath, SETTINGS_PATH.child(it)) } } @@ -97,35 +111,33 @@ object BackupHelper { return context.filesDir.listFiles { _, name -> name.endsWith(".jpg") }?.map { - ZipItem(it.absolutePath, "$IMAGES_PATH${File.separator}${it.name}") + ZipItem(it.absolutePath, IMAGES_PATH.child(it.name)) } } private fun getCustomArtistZipItems(context: Context): List { val zipItemList = mutableListOf() - val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + val sharedPrefPath = File(context.filesDir.parentFile, "shared_prefs") zipItemList.addAll( File(context.filesDir, "custom_artist_images") .listFiles()?.map { ZipItem( it.absolutePath, - "$CUSTOM_ARTISTS_PATH${File.separator}custom_artist_images${File.separator}${it.name}" + CUSTOM_ARTISTS_PATH.child("custom_artist_images").child(it.name) ) }?.toList() ?: listOf() ) - File(sharedPrefPath + File.separator + "custom_artist_image.xml").let { + File(sharedPrefPath, "custom_artist_image.xml").let { if (it.exists()) { zipItemList.add( ZipItem( it.absolutePath, - "$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml" + CUSTOM_ARTISTS_PATH.child("prefs").child("custom_artist_image.xml") ) ) } } - - return zipItemList } @@ -138,23 +150,19 @@ object BackupHelper { ZipInputStream(inputStream).use { var entry = it.nextEntry while (entry != null) { - if (entry.isDatabaseEntry() && contents.contains(PLAYLISTS)) { - restoreDatabase(context, it, entry) + if (entry.isPlaylistEntry() && contents.contains(PLAYLISTS)) { + restorePlaylists(it, entry) } else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) { restorePreferences(context, it, entry) } else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) { restoreImages(context, it, entry) - - } else if (entry.isCustomArtistImageEntry() && contents.contains( - CUSTOM_ARTIST_IMAGES - ) - ) { - restoreCustomArtistImages(context, it, entry) - restoreCustomArtistPrefs(context, it, entry) - } else if (entry.isQueueEntry() && contents.contains(QUEUE)) { - restoreQueueDatabase(context, it, entry) + } else if (entry.isCustomArtistEntry() && contents.contains(CUSTOM_ARTIST_IMAGES)) { + if (entry.isCustomArtistPrefEntry()) { + restoreCustomArtistPrefs(context, it, entry) + } else if (entry.isCustomArtistImageEntry()) { + restoreCustomArtistImages(context, it, entry) + } } - entry = it.nextEntry } } @@ -165,14 +173,11 @@ object BackupHelper { } private fun restoreImages(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { - val filePath = - context.filesDir.path + File.separator + zipEntry.getFileName() - BufferedOutputStream(FileOutputStream(filePath)).use { bos -> - val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) - var read: Int - while (zipIn.read(bytesIn).also { read = it } != -1) { - bos.write(bytesIn, 0, read) - } + val file = File( + context.filesDir.path, zipEntry.getFileName() + ) + BufferedOutputStream(FileOutputStream(file)).use { bos -> + zipIn.copyTo(bos) } } @@ -184,39 +189,38 @@ object BackupHelper { file.delete() } BufferedOutputStream(FileOutputStream(file)).use { bos -> - val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) - var read: Int - while (zipIn.read(bytesIn).also { read = it } != -1) { - bos.write(bytesIn, 0, read) - } + zipIn.copyTo(bos) } } - private fun restoreDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { - val filePath = - context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName() - BufferedOutputStream(FileOutputStream(filePath)).use { bos -> - val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) - var read: Int - while (zipIn.read(bytesIn).also { read = it } != -1) { - bos.write(bytesIn, 0, read) - } - } - } + private suspend fun restorePlaylists( + zipIn: ZipInputStream, + zipEntry: ZipEntry + ) { + val playlistName = zipEntry.getFileName().substringBeforeLast(".") + val songs = mutableListOf() - private fun restoreQueueDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { - PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) { - putInt("POSITION", 0) - } - val filePath = - context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName() - BufferedOutputStream(FileOutputStream(filePath)).use { bos -> - val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) - var read: Int - while (zipIn.read(bytesIn).also { read = it } != -1) { - bos.write(bytesIn, 0, read) + // Get songs from m3u playlist files + zipIn.bufferedReader().lineSequence().forEach { line -> + if (line.startsWith(File.separator)) { + if (File(line).exists()) { + songs.addAll(songRepository.songsByFilePath(line)) + } } } + val playlistEntity = repository.checkPlaylistExists(playlistName).firstOrNull() + if (playlistEntity != null) { + val songEntities = songs.map { + it.toSongEntity(playlistEntity.playListId) + } + repository.insertSongs(songEntities) + } else { + val playListId = repository.createPlaylist(PlaylistEntity(playlistName = playlistName)) + val songEntities = songs.map { + it.toSongEntity(playListId) + } + repository.insertSongs(songEntities) + } } private fun restoreCustomArtistImages( @@ -237,11 +241,7 @@ object BackupHelper { ) ) ).use { bos -> - val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) - var read: Int - while (zipIn.read(bytesIn).also { read = it } != -1) { - bos.write(bytesIn, 0, read) - } + zipIn.copyTo(bos) } } @@ -250,32 +250,30 @@ object BackupHelper { zipIn: ZipInputStream, zipEntry: ZipEntry ) { - val filePath = - context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + zipEntry.getFileName() - BufferedOutputStream(FileOutputStream(filePath)).use { bos -> - val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) - var read: Int - while (zipIn.read(bytesIn).also { read = it } != -1) { - bos.write(bytesIn, 0, read) - } + val file = + File(context.filesDir.parentFile, "shared_prefs".child(zipEntry.getFileName())) + BufferedOutputStream(FileOutputStream(file)).use { bos -> + zipIn.copyTo(bos) } } - val backupRootPath = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) - .toString() + "/RetroMusic/Backups/" + fun getBackupRoot(context: Context): File { + return File( + context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), + "RetroMusic/Backups" + ) + } + const val BACKUP_EXTENSION = "rmbak" const val APPEND_EXTENSION = ".$BACKUP_EXTENSION" - private const val DATABASES_PATH = "databases" - private const val QUEUE_PATH = "queue" + private const val PLAYLISTS_PATH = "Playlists" private const val SETTINGS_PATH = "prefs" private const val IMAGES_PATH = "userImages" private const val CUSTOM_ARTISTS_PATH = "artistImages" private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]" - private const val queueDatabase = "music_playback_state.db" - private fun ZipEntry.isDatabaseEntry(): Boolean { - return name.startsWith(DATABASES_PATH) + private fun ZipEntry.isPlaylistEntry(): Boolean { + return name.startsWith(PLAYLISTS_PATH) } private fun ZipEntry.isPreferenceEntry(): Boolean { @@ -286,6 +284,10 @@ object BackupHelper { return name.startsWith(IMAGES_PATH) } + private fun ZipEntry.isCustomArtistEntry(): Boolean { + return name.startsWith(CUSTOM_ARTISTS_PATH) + } + private fun ZipEntry.isCustomArtistImageEntry(): Boolean { return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("custom_artist_images") } @@ -294,12 +296,12 @@ object BackupHelper { return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs") } - private fun ZipEntry.isQueueEntry(): Boolean { - return name.startsWith(QUEUE_PATH) + private fun ZipEntry.getFileName(): String { + return name.substring(name.lastIndexOf(File.separator) + 1) } - private fun ZipEntry.getFileName(): String { - return name.substring(name.lastIndexOf(File.separator)) + fun getTimeStamp(): String { + return SimpleDateFormat("dd-MMM yyyy HHmmss", Locale.getDefault()).format(Date()) } } @@ -318,10 +320,13 @@ fun CharSequence.sanitize(): String { .replace("&", "_") } +fun String.child(child: String): String { + return this + File.separator + child +} + enum class BackupContent { SETTINGS, USER_IMAGES, CUSTOM_ARTIST_IMAGES, - PLAYLISTS, - QUEUE + PLAYLISTS } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt index 6bd424963..4101f89ef 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt @@ -459,7 +459,7 @@ object MusicPlayerRemote : KoinComponent { songFile = File(path) } if (songFile == null && uri.path != null) { - songFile = File(uri.path) + songFile = File(uri.path!!) } if (songFile != null) { songs = songRepository.songsByFilePath(songFile.absolutePath) diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt b/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt index 0771b2936..b77e39d09 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/SortOrder.kt @@ -134,7 +134,7 @@ class SortOrder { companion object { /* Artist song sort order A-Z */ - private const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER + const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER /* Artist song sort order Z-A */ const val SONG_Z_A = "$SONG_A_Z DESC" diff --git a/app/src/main/java/code/name/monkey/retromusic/lyrics/CoverLrcView.kt b/app/src/main/java/code/name/monkey/retromusic/lyrics/CoverLrcView.kt index 7f74fc8b3..34ce472e1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/lyrics/CoverLrcView.kt +++ b/app/src/main/java/code/name/monkey/retromusic/lyrics/CoverLrcView.kt @@ -169,10 +169,10 @@ class CoverLrcView @JvmOverloads constructor( mCurrentLine = centerLine invalidate() return true - } else { - callOnClick() - return true } + } else { + callOnClick() + return true } return super.onSingleTapConfirmed(e) } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt b/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt index faf133986..c9d7e42dc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt @@ -14,7 +14,9 @@ package code.name.monkey.retromusic.model +import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil data class Artist( val id: Long, @@ -29,7 +31,7 @@ data class Artist( name = artistName } - var name: String = "" + var name: String = "-" get() { val name = if (isAlbumArtist) getAlbumArtistName() else getArtistName() @@ -57,6 +59,39 @@ data class Artist( val songs: List get() = albums.flatMap { it.songs } + val sortedSongs: List + get() = songs.sortedWith( + when (PreferenceUtil.artistDetailSongSortOrder) { + SortOrder.ArtistSongSortOrder.SONG_A_Z -> { o1, o2 -> + o1.title.compareTo( + o2.title + ) + } + SortOrder.ArtistSongSortOrder.SONG_Z_A -> { o1, o2 -> + o2.title.compareTo( + o1.title + ) + } + SortOrder.ArtistSongSortOrder.SONG_ALBUM -> { o1, o2 -> + o1.albumName.compareTo( + o2.albumName + ) + } + SortOrder.ArtistSongSortOrder.SONG_YEAR -> { o1, o2 -> + o2.year.compareTo( + o1.year + ) + } + SortOrder.ArtistSongSortOrder.SONG_DURATION -> { o1, o2 -> + o1.duration.compareTo( + o2.duration + ) + } + else -> { + throw IllegalArgumentException("invalid ${PreferenceUtil.artistDetailSongSortOrder}") + } + }) + fun safeGetFirstAlbum(): Album { return albums.firstOrNull() ?: Album.empty } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt index 19ad179a1..f822540a9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt @@ -49,6 +49,4 @@ open class Playlist( result = 31 * result + name.hashCode() return result } - - } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt b/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt index 6f28e1ce1..9b6e19e11 100644 --- a/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt +++ b/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt @@ -55,7 +55,6 @@ class LibraryPreferenceDialog : DialogFragment() { .inflate(R.layout.preference_dialog_library_categories, null) val categoryAdapter = CategoryInfoAdapter() - categoryAdapter.categoryInfos = PreferenceUtil.libraryCategory val recyclerView = view.findViewById(R.id.recycler_view) recyclerView.layoutManager = LinearLayoutManager(activity) recyclerView.adapter = categoryAdapter diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistRepository.kt index 5f215670a..004dfa370 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/PlaylistRepository.kt @@ -17,7 +17,6 @@ package code.name.monkey.retromusic.repository import android.content.ContentResolver import android.database.Cursor import android.provider.BaseColumns -import android.provider.MediaStore import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.Playlists.* import android.provider.MediaStore.Audio.PlaylistsColumns @@ -120,13 +119,18 @@ class RealPlaylistRepository( private fun getPlaylistFromCursorImpl( cursor: Cursor ): Playlist { - val id = cursor.getLong(MediaStore.MediaColumns._ID) - val name = cursor.getString(NAME) - return Playlist(id, name) + val id = cursor.getLong(0) + val name = cursor.getString(1) + return if (name != null) { + Playlist(id, name) + } else { + Playlist.empty + } } override fun playlistSongs(playlistId: Long): List { val songs = arrayListOf() + if (playlistId == -1L) return songs val cursor = makePlaylistSongCursor(playlistId) if (cursor != null && cursor.moveToFirst()) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt index 6aca8cde7..ab72dfa15 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt @@ -130,6 +130,13 @@ object PreferenceUtil { ) set(value) = sharedPreferences.edit { putString(ALBUM_DETAIL_SONG_SORT_ORDER, value) } + var artistDetailSongSortOrder + get() = sharedPreferences.getStringOrDefault( + ARTIST_DETAIL_SONG_SORT_ORDER, + ArtistSongSortOrder.SONG_A_Z + ) + set(value) = sharedPreferences.edit { putString(ARTIST_DETAIL_SONG_SORT_ORDER, value) } + var songSortOrder get() = sharedPreferences.getStringOrDefault( SONG_SORT_ORDER, diff --git a/app/src/main/res/layout-land/activity_drive_mode.xml b/app/src/main/res/layout-land/activity_drive_mode.xml index 6a7fcc8cb..b3806a5b2 100644 --- a/app/src/main/res/layout-land/activity_drive_mode.xml +++ b/app/src/main/res/layout-land/activity_drive_mode.xml @@ -181,6 +181,7 @@ android:layout_height="wrap_content" android:layout_weight="8" android:maxHeight="3dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" android:progressTint="@color/md_white_1000" android:splitTrack="false" diff --git a/app/src/main/res/layout-land/fragment_circle_player.xml b/app/src/main/res/layout-land/fragment_circle_player.xml index cff4fcf3c..bfe771c6a 100644 --- a/app/src/main/res/layout-land/fragment_circle_player.xml +++ b/app/src/main/res/layout-land/fragment_circle_player.xml @@ -181,6 +181,7 @@ android:id="@+id/progressSlider" android:layout_width="0dp" android:layout_height="wrap_content" + android:paddingVertical="@dimen/seekbar_padding" app:layout_constraintBottom_toTopOf="@id/songInfo" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" diff --git a/app/src/main/res/layout/activity_drive_mode.xml b/app/src/main/res/layout/activity_drive_mode.xml index 31841df4d..efdb49f7a 100644 --- a/app/src/main/res/layout/activity_drive_mode.xml +++ b/app/src/main/res/layout/activity_drive_mode.xml @@ -188,6 +188,7 @@ android:layout_height="wrap_content" android:layout_weight="8" android:maxHeight="3dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" android:progressTint="@color/md_white_1000" android:splitTrack="false" diff --git a/app/src/main/res/layout/activity_restore.xml b/app/src/main/res/layout/activity_restore.xml index 5f5224af6..e9e922f8d 100644 --- a/app/src/main/res/layout/activity_restore.xml +++ b/app/src/main/res/layout/activity_restore.xml @@ -38,14 +38,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="48dp" - android:text="@string/databases_description" /> - - + android:text="@string/playlists" /> diff --git a/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml b/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml index 0938629a5..76b3ad3a5 100644 --- a/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml @@ -27,6 +27,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" diff --git a/app/src/main/res/layout/fragment_artist_content.xml b/app/src/main/res/layout/fragment_artist_content.xml index 6c3fb06d9..e727ed704 100644 --- a/app/src/main/res/layout/fragment_artist_content.xml +++ b/app/src/main/res/layout/fragment_artist_content.xml @@ -1,5 +1,5 @@ - + app:layout_constraintTop_toBottomOf="@id/albumRecyclerView"/> + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_blur_player_playback_controls.xml b/app/src/main/res/layout/fragment_blur_player_playback_controls.xml index 775858d14..b17263933 100644 --- a/app/src/main/res/layout/fragment_blur_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_blur_player_playback_controls.xml @@ -41,6 +41,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" android:splitTrack="false" app:layout_constraintEnd_toStartOf="@id/songTotalTime" diff --git a/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml b/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml index c823c9f3e..719c6dbd3 100644 --- a/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml @@ -53,6 +53,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:paddingStart="20dp" android:paddingEnd="20dp" android:progressDrawable="@drawable/color_progress_seek" diff --git a/app/src/main/res/layout/fragment_card_player_playback_controls.xml b/app/src/main/res/layout/fragment_card_player_playback_controls.xml index 070149f99..6c019c1dc 100644 --- a/app/src/main/res/layout/fragment_card_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_card_player_playback_controls.xml @@ -28,10 +28,11 @@ + android:layout_weight="1" + android:paddingVertical="@dimen/seekbar_padding" /> diff --git a/app/src/main/res/layout/fragment_color_player_playback_controls.xml b/app/src/main/res/layout/fragment_color_player_playback_controls.xml index c2a580b88..207b52bf6 100644 --- a/app/src/main/res/layout/fragment_color_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_color_player_playback_controls.xml @@ -28,6 +28,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" diff --git a/app/src/main/res/layout/fragment_fit_playback_controls.xml b/app/src/main/res/layout/fragment_fit_playback_controls.xml index 982c5945b..888a68f01 100644 --- a/app/src/main/res/layout/fragment_fit_playback_controls.xml +++ b/app/src/main/res/layout/fragment_fit_playback_controls.xml @@ -37,6 +37,7 @@ style="@style/MusicProgressSlider" android:layout_width="0dp" android:layout_height="match_parent" + android:paddingVertical="@dimen/seekbar_padding" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" tools:ignore="RtlHardcoded,UnusedAttribute" /> diff --git a/app/src/main/res/layout/fragment_flat_player_playback_controls.xml b/app/src/main/res/layout/fragment_flat_player_playback_controls.xml index bee59f8f8..85dda2d01 100644 --- a/app/src/main/res/layout/fragment_flat_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_flat_player_playback_controls.xml @@ -27,6 +27,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="8" + android:paddingVertical="@dimen/seekbar_padding" android:splitTrack="false" android:thumb="@drawable/switch_square" app:layout_constraintEnd_toStartOf="@id/songTotalTime" diff --git a/app/src/main/res/layout/fragment_full_player_controls.xml b/app/src/main/res/layout/fragment_full_player_controls.xml index 9baa5c70c..7e3828311 100644 --- a/app/src/main/res/layout/fragment_full_player_controls.xml +++ b/app/src/main/res/layout/fragment_full_player_controls.xml @@ -99,6 +99,7 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" diff --git a/app/src/main/res/layout/fragment_gradient_controls.xml b/app/src/main/res/layout/fragment_gradient_controls.xml index ae6b98ee7..59f4ff77e 100644 --- a/app/src/main/res/layout/fragment_gradient_controls.xml +++ b/app/src/main/res/layout/fragment_gradient_controls.xml @@ -101,6 +101,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="16dp" + android:paddingVertical="@dimen/seekbar_padding" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintTop_toBottomOf="@id/titleContainer" diff --git a/app/src/main/res/layout/fragment_lock_screen_playback_controls.xml b/app/src/main/res/layout/fragment_lock_screen_playback_controls.xml index 95fe798f5..f4808a1b2 100644 --- a/app/src/main/res/layout/fragment_lock_screen_playback_controls.xml +++ b/app/src/main/res/layout/fragment_lock_screen_playback_controls.xml @@ -30,6 +30,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" diff --git a/app/src/main/res/layout/fragment_material_playback_controls.xml b/app/src/main/res/layout/fragment_material_playback_controls.xml index 60f6d305e..5d02d286f 100644 --- a/app/src/main/res/layout/fragment_material_playback_controls.xml +++ b/app/src/main/res/layout/fragment_material_playback_controls.xml @@ -29,6 +29,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" diff --git a/app/src/main/res/layout/fragment_peak_control_player.xml b/app/src/main/res/layout/fragment_peak_control_player.xml index c050083f3..3af7dce4d 100644 --- a/app/src/main/res/layout/fragment_peak_control_player.xml +++ b/app/src/main/res/layout/fragment_peak_control_player.xml @@ -40,6 +40,7 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:maxHeight="2dp" + android:paddingVertical="@dimen/seekbar_padding" android:progressDrawable="@drawable/color_progress_seek" app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintStart_toEndOf="@id/songCurrentProgress" diff --git a/app/src/main/res/layout/fragment_plain_controls_fragment.xml b/app/src/main/res/layout/fragment_plain_controls_fragment.xml index eaa052aa3..d23f7cae9 100644 --- a/app/src/main/res/layout/fragment_plain_controls_fragment.xml +++ b/app/src/main/res/layout/fragment_plain_controls_fragment.xml @@ -33,6 +33,7 @@ app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintTop_toTopOf="parent" tools:ignore="RtlHardcoded,UnusedAttribute" + android:paddingVertical="@dimen/seekbar_padding" tools:progress="20" /> - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml index 2081dd35f..d2111c843 100644 --- a/app/src/main/res/values-ar-rSA/strings.xml +++ b/app/src/main/res/values-ar-rSA/strings.xml @@ -146,7 +146,6 @@ يستمع حالياً إلى %1$s لـ %2$s Custom Artist Images أسود قليلاً - Databases (Playlists, History, Most Played, etc.) حذف قائمة التشغيل %1$s؟]]> حذف قوائم التشغيل diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 5888e4f96..3e12aec33 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -140,7 +140,6 @@ Aktuálně posloucháš %1$s od %2$s. Custom Artist Images Tmavá - Databases (Playlists, History, Most Played, etc.) Smazat seznam skladeb %1$s?]]> Smazat seznamy skladeb diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index d5054be18..57e8b115c 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -136,7 +136,6 @@ Spielt zur Zeit %1$s von %2$s. Custom Artist Images Dunkel - Databases (Playlists, History, Most Played, etc.) Playlist löschen %1$s löschen?]]> Playlisten löschen diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml index feea07f59..14523ba85 100644 --- a/app/src/main/res/values-el-rGR/strings.xml +++ b/app/src/main/res/values-el-rGR/strings.xml @@ -136,7 +136,6 @@ Παίζει το %1$s από τους %2$s. Custom Artist Images Κάπως Σκούρο - Databases (Playlists, History, Most Played, etc.) Διαγραφή playlist %1$s?]]> Διαγραφή αυτών των λιστών αναπαραγωγής diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 1c4c0921f..eb91a4cac 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -136,7 +136,6 @@ Actualmente estás escuchando %1$s de %2$s. Custom Artist Images Un poco Oscuro - Databases (Playlists, History, Most Played, etc.) Eliminar lista de reproducción %1$s?]]> Eliminar listas de reproducción diff --git a/app/src/main/res/values-fa-rIR/strings.xml b/app/src/main/res/values-fa-rIR/strings.xml index 2992c1764..791030970 100644 --- a/app/src/main/res/values-fa-rIR/strings.xml +++ b/app/src/main/res/values-fa-rIR/strings.xml @@ -136,7 +136,6 @@ در حال حاضر شما به s$1% از s$2% گوش می‌دهید. Custom Artist Images تقریبا مشکی - Databases (Playlists, History, Most Played, etc.) حذف کردن پلی لیست %1$s??]]> حذف کردن پلی لیست ها diff --git a/app/src/main/res/values-fil-rPH/strings.xml b/app/src/main/res/values-fil-rPH/strings.xml index 7008b44be..5313f5b18 100644 --- a/app/src/main/res/values-fil-rPH/strings.xml +++ b/app/src/main/res/values-fil-rPH/strings.xml @@ -136,7 +136,6 @@ Kasalukuyang nakikinig sa %1$s ni %2$s. Custom Artist Images Medyo Madilim - Databases (Playlists, History, Most Played, etc.) Tanggalin ang playlist %1$s?]]> Tanggalin ang mga playlist diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index e0f4d8824..c9046d5ba 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -136,7 +136,6 @@ Vous écoutez actuellement %1$s par %2$s. Custom Artist Images Plutôt Sombre - Databases (Playlists, History, Most Played, etc.) Supprimer la liste de lecture %1$s ?]]> Supprimer les listes de lecture diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index 6f28ea543..a73ad2937 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -136,7 +136,6 @@ Currently listening to %1$s by %2$s. Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) Delete playlist %1$s?]]> Delete playlists diff --git a/app/src/main/res/values-hr-rHR/strings.xml b/app/src/main/res/values-hr-rHR/strings.xml index c7ecadb83..e4d7e6c5e 100644 --- a/app/src/main/res/values-hr-rHR/strings.xml +++ b/app/src/main/res/values-hr-rHR/strings.xml @@ -138,7 +138,6 @@ Currently listening to %1$s by %2$s. Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) Delete playlist %1$s?]]> Delete playlists diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index c78241bab..bd7c0105e 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -136,7 +136,6 @@ Jelenleg %1$s hallgatása %2$s által. Custom Artist Images Kissé sötét - Databases (Playlists, History, Most Played, etc.) Lejátszási lista törlése %1$s lejátszási listát?]]> Lejátszási listák törlése diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index 1c3eaa288..30fef24a9 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -137,7 +137,6 @@ https://play.google.com/store/apps/details?id=%s Ascoltando attualmente %1$s di %2$s. Immagini dell\'Artista Personalizzate Scuro - Database (Playlist, Cronologia, Più riprodotti, ecc.) Elimina playlist %1$s?]]> Elimina playlist diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 4268eadde..4a759fb1a 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -135,7 +135,6 @@ 現在、 %1$s によって %2$s で聴いています。 Custom Artist Images ダーク - Databases (Playlists, History, Most Played, etc.) プレイリストを削除 %1$s を削除しますか?]]> プレイリストを削除 diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 4566f6978..04b9d5c16 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -134,7 +134,6 @@ 현재 %2$s의 %1$s를 듣는 중입니다. Custom Artist Images 카인다 다크 - Databases (Playlists, History, Most Played, etc.) 재생목록 삭제 %1$s 재생목록을 삭제하시겠습니까?]]> 재생목록 삭제 diff --git a/app/src/main/res/values-ml-rIN/strings.xml b/app/src/main/res/values-ml-rIN/strings.xml index 85ffd6afb..eaa2ac2f0 100644 --- a/app/src/main/res/values-ml-rIN/strings.xml +++ b/app/src/main/res/values-ml-rIN/strings.xml @@ -136,7 +136,6 @@ Currently listening to %1$s by %2$s. Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) Delete playlist %1$s?]]> Delete playlists diff --git a/app/src/main/res/values-my-rMM/strings.xml b/app/src/main/res/values-my-rMM/strings.xml index 83e928281..087ab7858 100644 --- a/app/src/main/res/values-my-rMM/strings.xml +++ b/app/src/main/res/values-my-rMM/strings.xml @@ -134,7 +134,6 @@ %2$s သီဆိုထားသော %1$s ကိုယခုနားထောင်နေသည် Custom Artist Images အနက်ရောင်ဆန်ဆန် - Databases (Playlists, History, Most Played, etc.) Playlist ဖျက်ခြင်း %1$s Playlist ကိုဖျက်မှာလား]]> Playlist များဖျက်ခြင်း diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml index 501097859..9ed36b5e4 100644 --- a/app/src/main/res/values-nl-rNL/strings.xml +++ b/app/src/main/res/values-nl-rNL/strings.xml @@ -136,7 +136,6 @@ Nu luisterend naar %1$s van %2$s. Custom Artist Images Soort van donker - Databases (Playlists, History, Most Played, etc.) Afspeellijst verwijderen %1$s verwijderen?]]> Afspeellijsten verwijderen diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml index b22b9ac30..4d2c2b212 100644 --- a/app/src/main/res/values-no-rNO/strings.xml +++ b/app/src/main/res/values-no-rNO/strings.xml @@ -136,7 +136,6 @@ Currently listening to %1$s by %2$s. Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) Delete playlist %1$s?]]> Delete playlists diff --git a/app/src/main/res/values-or-rIN/strings.xml b/app/src/main/res/values-or-rIN/strings.xml index 0ae540bef..1b3d4a237 100644 --- a/app/src/main/res/values-or-rIN/strings.xml +++ b/app/src/main/res/values-or-rIN/strings.xml @@ -136,7 +136,6 @@ Currently listening to %1$s by %2$s. Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) Delete playlist %1$s?]]> Delete playlists diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index dcc526560..59d353453 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -140,7 +140,6 @@ Aktualnie odtwarzane %1$s wykonawcy %2$s. Custom Artist Images Dość ciemny - Databases (Playlists, History, Most Played, etc.) Usuń playlistę %1$s?]]> Usuń playlisty diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c563a4586..ac96569f4 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -136,7 +136,6 @@ Atualmente ouvindo %1$s por %2$s. Custom Artist Images Meio escuro - Databases (Playlists, History, Most Played, etc.) Excluir playlist %1$s?]]> Excluir playlists diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index cc1a8d6f6..c01ccaaf7 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -136,7 +136,6 @@ A ouvir %1$s de %2$s. Custom Artist Images Meio escuro - Databases (Playlists, History, Most Played, etc.) Eliminar lista %1$s?]]> Eliminar listas diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index f4b9b9859..c834ca77a 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -138,7 +138,6 @@ Acum ascultați %1$s de %2$s. Custom Artist Images Suriu - Databases (Playlists, History, Most Played, etc.) Șterge playlist %1$s?]]> Șterge playlist-uri diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index d6b4fd747..2b056856b 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -140,7 +140,6 @@ Сейчас играет %1$s от %2$s. Custom Artist Images Тёмная - Databases (Playlists, History, Most Played, etc.) Удалить плейлист %1$s?]]> Удалить плейлисты diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index 08b85d0b3..0657b0e50 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -138,7 +138,6 @@ Trenutno se reprodukuje %1$s izvodjaca %2$s Custom Artist Images Kao tamno - Databases (Playlists, History, Most Played, etc.) Izbrisi plejlistu %1$s plejlistu?]]> Izbrisi plejlistu diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index cfabc1395..32b494e09 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -136,7 +136,6 @@ Lyssnar just nu på %1$s av %2$s. Custom Artist Images Ganska mörkt - Databases (Playlists, History, Most Played, etc.) Radera spellista %1$s?]]> Radera spellistor diff --git a/app/src/main/res/values-ta-rIN/strings.xml b/app/src/main/res/values-ta-rIN/strings.xml index 474260cab..2634e7bd3 100644 --- a/app/src/main/res/values-ta-rIN/strings.xml +++ b/app/src/main/res/values-ta-rIN/strings.xml @@ -136,7 +136,6 @@ Currently listening to %1$s by %2$s. Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) Delete playlist %1$s?]]> Delete playlists diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index ad90fd815..f48a24e78 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -134,7 +134,6 @@ กำลังฟัง %1$s ของ %2$s Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) ลบเพลย์ลิสต์ %1$s?]]> ลบเพลย์ลิสต์ diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index 0742ab4b7..ed827ded6 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -136,7 +136,6 @@ Şu anda %2$s şarkıcısından %1$s dinleniyor. Custom Artist Images Koyu - Databases (Playlists, History, Most Played, etc.) Çalma listesini sil %1$s silinsin mi?]]> Çalma listelerini sil diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 8907d217c..5adaab45f 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -140,7 +140,6 @@ Зараз грає %1$s від %2$s. Custom Artist Images Майже темна - Databases (Playlists, History, Most Played, etc.) Видалити список відтворення %1$s?]]> Видалити списки відтворення diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index b3372da9c..7eaf75366 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -135,7 +135,6 @@ tiếp tục xảy ra hãy \"Xóa dữ liệu ứng dụng\" Đang nghe %1$s bởi %2$s. Custom Artist Images Xám đen - Databases (Playlists, History, Most Played, etc.) Xoá danh sách phát %1$s?]]> Xoá danh sách phát diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 267726dc2..3a2f33681 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -134,7 +134,6 @@ 正在收听 %2$s 的 %1$s。 自定义艺术家图像 深色 - 数据库(播放列表、播放历史、最多播放等) 删除播放列表 %1$s 吗?]]> 删除播放列表 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 29a512be3..85e626518 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -134,7 +134,6 @@ 我正在聽 %2$s 的 %1$s Custom Artist Images 暗沉 - Databases (Playlists, History, Most Played, etc.) 刪除播放清單 %1$s 嗎?]]> 刪除多個播放清單 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a3f2064cf..bf5ba4946 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -64,4 +64,6 @@ 24dp 40dp 16dp + + 16dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b66f5c1a7..6afb0f685 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -138,7 +138,6 @@ Currently listening to %1$s by %2$s. Custom Artist Images Kinda Dark - Databases (Playlists, History, Most Played, etc.) Delete playlist %1$s?]]>