Merge pull request #1211 from prathameshmm02/dev

Fixed some crashes and bugs
This commit is contained in:
Daksh P. Jain 2021-12-31 20:24:26 +05:30 committed by GitHub
commit afc309a296
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
89 changed files with 519 additions and 419 deletions

View file

@ -57,6 +57,8 @@ android {
abortOnError false abortOnError false
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
@ -149,6 +151,8 @@ dependencies {
kapt 'com.github.bumptech.glide:compiler:4.12.0' kapt 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.github.bumptech.glide:okhttp3-integration: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.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0' implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0'

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name" translatable="false">Retro Music-Debug</string>
<bool name="md3_available">true</bool> <bool name="md3_available">true</bool>
<bool name="allowBackup">false</bool> <bool name="allowBackup">false</bool>
</resources> </resources>

View file

@ -135,6 +135,7 @@ const val LOCK_SCREEN = "lock_screen"
const val ALBUM_ARTISTS_ONLY = "album_artists_only" const val ALBUM_ARTISTS_ONLY = "album_artists_only"
const val ALBUM_ARTIST = "album_artist" const val ALBUM_ARTIST = "album_artist"
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order" 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 LYRICS_OPTIONS = "lyrics_tab_position"
const val CHOOSE_EQUALIZER = "choose_equalizer" const val CHOOSE_EQUALIZER = "choose_equalizer"
const val EQUALIZER = "equalizer" const val EQUALIZER = "equalizer"

View file

@ -62,7 +62,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
if (!hasPermissions()) { if (!hasPermissions()) {
findNavController(R.id.fragment_container).navigate(R.id.permissionFragment) 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) NavigationUtil.gotoWhatNews(this)
} }
} }

View file

@ -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<CategoryInfoAdapter.ViewHolder>
implements SwipeAndDragHelper.ActionCompletionContract {
private List<CategoryInfo> 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<CategoryInfo> getCategoryInfos() {
return categoryInfos;
}
public void setCategoryInfos(@NonNull List<CategoryInfo> 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);
}
}
}

View file

@ -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<CategoryInfoAdapter.ViewHolder>(),
ActionCompletionContract {
var categoryInfos: MutableList<CategoryInfo> =
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)
}
}

View file

@ -14,6 +14,7 @@
*/ */
package code.name.monkey.retromusic.adapter package code.name.monkey.retromusic.adapter
import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -21,7 +22,7 @@ import android.view.ViewOutlineProvider
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R 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.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
@ -38,7 +39,6 @@ import java.util.*
class GenreAdapter( class GenreAdapter(
private val activity: FragmentActivity, private val activity: FragmentActivity,
var dataSet: List<Genre>, var dataSet: List<Genre>,
private val mItemLayoutRes: Int,
private val listener: IGenreClickListener private val listener: IGenreClickListener
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() { ) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
@ -51,13 +51,13 @@ class GenreAdapter(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 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) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val genre = dataSet[position] val genre = dataSet[position]
holder.title?.text = genre.name holder.binding.title.text = genre.name
holder.text?.text = String.format( holder.binding.text.text = String.format(
Locale.getDefault(), Locale.getDefault(),
"%d %s", "%d %s",
genre.songCount, genre.songCount,
@ -72,33 +72,39 @@ class GenreAdapter(
.asBitmapPalette() .asBitmapPalette()
.load(RetroGlideExtension.getSongModel(genreSong)) .load(RetroGlideExtension.getSongModel(genreSong))
.songCoverOptions(genreSong) .songCoverOptions(genreSong)
.into(object : RetroMusicColoredTarget(holder.image!!) { .into(object : RetroMusicColoredTarget(holder.binding.image) {
override fun onColorReady(colors: MediaNotificationProcessor) { override fun onColorReady(colors: MediaNotificationProcessor) {
setColors(holder, colors) setColors(holder, colors)
} }
}) })
// Just for a bit of shadow around image // 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) { private fun setColors(holder: ViewHolder, color: MediaNotificationProcessor) {
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor) holder.binding.imageContainerCard.setCardBackgroundColor(color.backgroundColor)
holder.title?.setTextColor(color.primaryTextColor) holder.binding.title.setTextColor(color.primaryTextColor)
holder.text?.setTextColor(color.secondaryTextColor) holder.binding.text.setTextColor(color.secondaryTextColor)
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return dataSet.size return dataSet.size
} }
@SuppressLint("NotifyDataSetChanged")
fun swapDataSet(list: List<Genre>) { fun swapDataSet(list: List<Genre>) {
dataSet = list dataSet = list
notifyDataSetChanged() 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?) { override fun onClick(v: View?) {
listener.onClickGenre(dataSet[layoutPosition], itemView) listener.onClickGenre(dataSet[layoutPosition], itemView)
} }
init {
itemView.setOnClickListener(this)
}
} }
} }

View file

@ -248,7 +248,6 @@ class HomeAdapter(
val genreAdapter = GenreAdapter( val genreAdapter = GenreAdapter(
activity, activity,
home.arrayList as List<Genre>, home.arrayList as List<Genre>,
R.layout.item_grid_genre,
this@HomeAdapter this@HomeAdapter
) )
recyclerView.apply { recyclerView.apply {

View file

@ -1,15 +1,14 @@
package code.name.monkey.retromusic.adapter.backup package code.name.monkey.retromusic.adapter.backup
import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.ItemListBackupBinding
import java.io.File import java.io.File
@ -21,27 +20,27 @@ class BackupAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return 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) { 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 override fun getItemCount(): Int = dataSet.size
@SuppressLint("NotifyDataSetChanged")
fun swapDataset(dataSet: List<File>) { fun swapDataset(dataSet: List<File>) {
this.dataSet = ArrayList(dataSet) this.dataSet = ArrayList(dataSet)
notifyDataSetChanged() notifyDataSetChanged()
} }
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class ViewHolder(val binding: ItemListBackupBinding) :
val title: TextView = itemView.findViewById(R.id.title) RecyclerView.ViewHolder(binding.root) {
val menu: AppCompatImageView = itemView.findViewById(R.id.menu)
init { init {
menu.setOnClickListener { view -> binding.menu.setOnClickListener { view ->
val popupMenu = PopupMenu(activity, view) val popupMenu = PopupMenu(activity, view)
popupMenu.inflate(R.menu.menu_backup) popupMenu.inflate(R.menu.menu_backup)
popupMenu.setOnMenuItemClickListener { menuItem -> popupMenu.setOnMenuItemClickListener { menuItem ->

View file

@ -255,11 +255,13 @@ class LibraryViewModel(
} }
repository.insertSongs(songEntities) repository.insertSongs(songEntities)
} else { } else {
val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name)) if (playlist != Playlist.empty){
val songEntities = playlist.getSongs().map { val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name))
it.toSongEntity(playListId) val songEntities = playlist.getSongs().map {
it.toSongEntity(playListId)
}
repository.insertSongs(songEntities)
} }
repository.insertSongs(songEntities)
} }
forceReload(Playlists) forceReload(Playlists)
} }

View file

@ -5,11 +5,9 @@ import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.Spanned import android.text.Spanned
import android.view.Menu import android.view.*
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.appcompat.widget.PopupMenu
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.view.ViewCompat 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.RetroGlideExtension
import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.glide.SingleColorTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote 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.IAlbumClickListener
import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabCallback
import code.name.monkey.retromusic.interfaces.ICabHolder 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.Result
import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.network.model.LastFmArtist
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.*
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialcab.attached.AttachedCab import com.afollestad.materialcab.attached.AttachedCab
import com.afollestad.materialcab.attached.destroy import com.afollestad.materialcab.attached.destroy
import com.afollestad.materialcab.attached.isActive import com.afollestad.materialcab.attached.isActive
@ -71,6 +67,9 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
private var lang: String? = null private var lang: String? = null
private var biography: Spanned? = null private var biography: Spanned? = null
private val savedSongSortOrder: String
get() = PreferenceUtil.artistDetailSongSortOrder
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform().apply { sharedElementEnterTransition = MaterialContainerTransform().apply {
@ -101,7 +100,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
setupRecyclerView() setupRecyclerView()
binding.fragmentArtistContent.playAction.apply { binding.fragmentArtistContent.playAction.apply {
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } setOnClickListener { MusicPlayerRemote.openQueue(artist.sortedSongs, 0, true) }
} }
binding.fragmentArtistContent.shuffleAction.apply { binding.fragmentArtistContent.shuffleAction.apply {
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) } setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
@ -121,6 +120,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
requireActivity().onBackPressed() requireActivity().onBackPressed()
} }
} }
setupSongSortButton()
binding.appBarLayout?.statusBarForeground = binding.appBarLayout?.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext()) MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
@ -168,7 +168,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
) )
binding.fragmentArtistContent.songTitle.text = songText binding.fragmentArtistContent.songTitle.text = songText
binding.fragmentArtistContent.albumTitle.text = albumText binding.fragmentArtistContent.albumTitle.text = albumText
songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber }) songAdapter.swapDataSet(artist.sortedSongs)
albumAdapter.swapDataSet(artist.albums) albumAdapter.swapDataSet(artist.albums)
} }
@ -289,6 +289,53 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
return true 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?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when (requestCode) { when (requestCode) {

View file

@ -17,6 +17,8 @@ import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.backup.BackupAdapter import code.name.monkey.retromusic.adapter.backup.BackupAdapter
import code.name.monkey.retromusic.databinding.FragmentBackupBinding 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.BackupHelper
import code.name.monkey.retromusic.helper.sanitize import code.name.monkey.retromusic.helper.sanitize
import code.name.monkey.retromusic.util.BackupUtil import code.name.monkey.retromusic.util.BackupUtil
@ -45,7 +47,7 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
else else
backupAdapter?.swapDataset(listOf()) backupAdapter?.swapDataset(listOf())
} }
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) { val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
it?.let { it?.let {
@ -55,6 +57,8 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
} }
} }
} }
binding.createBackup.accentOutlineColor()
binding.restoreBackup.accentColor()
binding.createBackup.setOnClickListener { binding.createBackup.setOnClickListener {
showCreateBackupDialog() showCreateBackupDialog()
} }
@ -91,11 +95,11 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
MaterialDialog(requireContext()).show { MaterialDialog(requireContext()).show {
cornerRadius(res = R.dimen.m3_card_corner_radius) cornerRadius(res = R.dimen.m3_card_corner_radius)
title(res = R.string.action_rename) title(res = R.string.action_rename)
input(prefill = System.currentTimeMillis().toString()) { _, text -> input(prefill = BackupHelper.getTimeStamp()) { _, text ->
// Text submitted with the action button // Text submitted with the action button
lifecycleScope.launch { lifecycleScope.launch {
BackupHelper.createBackup(requireContext(), text.sanitize()) BackupHelper.createBackup(requireContext(), text.sanitize())
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
} }
} }
positiveButton(android.R.string.ok) positiveButton(android.R.string.ok)
@ -125,7 +129,7 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
return true return true
} }
R.id.action_share -> { R.id.action_share -> {
@ -143,10 +147,10 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
input(prefill = file.nameWithoutExtension) { _, text -> input(prefill = file.nameWithoutExtension) { _, text ->
// Text submitted with the action button // Text submitted with the action button
val renamedFile = val renamedFile =
File(file.parent + File.separator + text + BackupHelper.APPEND_EXTENSION) File(file.parent, "$text${BackupHelper.APPEND_EXTENSION}")
if (!renamedFile.exists()) { if (!renamedFile.exists()) {
file.renameTo(renamedFile) file.renameTo(renamedFile)
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
} else { } else {
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),

View file

@ -1,6 +1,7 @@
package code.name.monkey.retromusic.fragments.backup package code.name.monkey.retromusic.fragments.backup
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -19,8 +20,8 @@ class BackupViewModel : ViewModel() {
private val backupsMutableLiveData = MutableLiveData<List<File>>() private val backupsMutableLiveData = MutableLiveData<List<File>>()
val backupsLiveData: LiveData<List<File>> = backupsMutableLiveData val backupsLiveData: LiveData<List<File>> = backupsMutableLiveData
fun loadBackups() { fun loadBackups(context: Context) {
File(BackupHelper.backupRootPath).listFiles { _, name -> BackupHelper.getBackupRoot(context).listFiles { _, name ->
return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION) return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION)
}?.toList()?.let { }?.toList()?.let {
backupsMutableLiveData.value = it backupsMutableLiveData.value = it
@ -29,13 +30,16 @@ class BackupViewModel : ViewModel() {
suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List<BackupContent>) { suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List<BackupContent>) {
BackupHelper.restoreBackup(activity, inputStream, contents) BackupHelper.restoreBackup(activity, inputStream, contents)
withContext(Dispatchers.Main) { if (contents.contains(BackupContent.SETTINGS) or contents.contains(BackupContent.CUSTOM_ARTIST_IMAGES)) {
val intent = Intent( // We have to restart App when Preferences i.e. Settings or Artist Images are to be restored
activity, withContext(Dispatchers.Main) {
MainActivity::class.java val intent = Intent(
) activity,
activity.startActivity(intent) MainActivity::class.java
exitProcess(0) )
activity.startActivity(intent)
exitProcess(0)
}
} }
} }
} }

View file

@ -3,9 +3,11 @@ package code.name.monkey.retromusic.fragments.backup
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.view.ViewGroup
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.databinding.ActivityRestoreBinding import code.name.monkey.retromusic.databinding.ActivityRestoreBinding
import code.name.monkey.retromusic.helper.BackupContent import code.name.monkey.retromusic.helper.BackupContent
@ -27,6 +29,7 @@ class RestoreActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityRestoreBinding.inflate(layoutInflater) binding = ActivityRestoreBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setWidth()
val backupUri = intent?.data val backupUri = intent?.data
binding.backupName.setText(getFileName(backupUri)) binding.backupName.setText(getFileName(backupUri))
binding.cancelButton.setOnClickListener { binding.cancelButton.setOnClickListener {
@ -35,7 +38,6 @@ class RestoreActivity : AppCompatActivity() {
binding.restoreButton.setOnClickListener { binding.restoreButton.setOnClickListener {
val backupContents = mutableListOf<BackupContent>() val backupContents = mutableListOf<BackupContent>()
if (binding.checkSettings.isChecked) backupContents.add(SETTINGS) if (binding.checkSettings.isChecked) backupContents.add(SETTINGS)
if (binding.checkQueue.isChecked) backupContents.add(QUEUE)
if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS) if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS)
if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES) if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES)
if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES) if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES)
@ -67,17 +69,13 @@ class RestoreActivity : AppCompatActivity() {
return uri.lastPathSegment return uri.lastPathSegment
} }
"content" -> { "content" -> {
val proj = arrayOf(MediaStore.Images.Media.TITLE) val proj = arrayOf(MediaStore.Files.FileColumns.DISPLAY_NAME)
contentResolver.query( contentResolver.query(
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { uri, proj, null, null, null
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}, proj, null, null, null
)?.use { cursor -> )?.use { cursor ->
if (cursor.count != 0) { if (cursor.count != 0) {
val columnIndex: Int = val columnIndex: Int =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE) cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
cursor.moveToFirst() cursor.moveToFirst()
return cursor.getString(columnIndex) return cursor.getString(columnIndex)
} }
@ -86,4 +84,9 @@ class RestoreActivity : AppCompatActivity() {
} }
return "Backup" return "Backup"
} }
private fun setWidth() {
val width = resources.displayMetrics.widthPixels * 0.8
binding.root.updateLayoutParams<ViewGroup.LayoutParams> { this.width = width.toInt() }
}
} }

View file

@ -164,7 +164,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) { if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height_expanded)) binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height_expanded))
} else { } else {
binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height)) binding.recyclerView.updatePadding(bottom = dip(R.dimen.bottom_nav_height))
} }
} }

View file

@ -62,7 +62,7 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
override fun createAdapter(): GenreAdapter { override fun createAdapter(): GenreAdapter {
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet 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) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {

View file

@ -29,6 +29,7 @@ import android.widget.Toast
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_BANNER
import code.name.monkey.retromusic.Constants.USER_PROFILE 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.ImagePicker
import com.github.dhaval2404.imagepicker.constant.ImageProvider import com.github.dhaval2404.imagepicker.constant.ImageProvider
import com.google.android.material.transition.MaterialContainerTransform import com.google.android.material.transition.MaterialContainerTransform
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -202,15 +202,15 @@ class UserInfoFragment : Fragment() {
} }
private fun saveImage(bitmap: Bitmap, fileName: String) { private fun saveImage(bitmap: Bitmap, fileName: String) {
CoroutineScope(Dispatchers.IO).launch { lifecycleScope.launch(Dispatchers.IO) {
val appDir = requireContext().filesDir val appDir = requireContext().filesDir
val file = File(appDir, fileName) val file = File(appDir, fileName)
var successful = false var successful = false
runCatching { runCatching {
val os = BufferedOutputStream(FileOutputStream(file)) BufferedOutputStream(FileOutputStream(file)).use {
successful = ImageUtil.resizeBitmap(bitmap, 2048) successful = ImageUtil.resizeBitmap(bitmap, 2048)
.compress(Bitmap.CompressFormat.WEBP, 100, os) .compress(Bitmap.CompressFormat.WEBP, 100, it)
withContext(Dispatchers.IO) { os.close() } }
}.onFailure { }.onFailure {
it.printStackTrace() it.printStackTrace()
} }

View file

@ -14,12 +14,13 @@
*/ */
package code.name.monkey.retromusic.fragments.player package code.name.monkey.retromusic.fragments.player
import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.view.isInvisible import androidx.core.animation.doOnEnd
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager 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.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment 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.fragments.base.goToLyrics
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
@ -223,8 +223,13 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} }
private fun showLyrics(visible: Boolean) { private fun showLyrics(visible: Boolean) {
lrcView.isVisible = visible ObjectAnimator.ofFloat(lrcView, View.ALPHA, if (visible) 1F else 0F).apply {
viewPager.isInvisible = visible doOnEnd {
lrcView.isVisible = visible
}
start()
}
ObjectAnimator.ofFloat(viewPager, View.ALPHA, if (visible) 0F else 1F).start()
} }
private fun maybeInitLyrics() { private fun maybeInitLyrics() {
@ -233,8 +238,6 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) { if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) {
showLyrics(true) showLyrics(true)
progressViewUpdateHelper?.start() progressViewUpdateHelper?.start()
lrcView.animate().alpha(1f).duration =
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
} else { } else {
showLyrics(false) showLyrics(false)
progressViewUpdateHelper?.stop() progressViewUpdateHelper?.stop()

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.player.color
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.view.View import android.view.View
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
@ -60,11 +61,13 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
_binding?.root?.setBackgroundColor(color.backgroundColor) _binding?.root?.setBackgroundColor(color.backgroundColor)
} }
animator.start() animator.start()
ToolbarContentTintHelper.colorizeToolbar( Handler().post {
binding.playerToolbar, ToolbarContentTintHelper.colorizeToolbar(
color.secondaryTextColor, binding.playerToolbar,
requireActivity() color.secondaryTextColor,
) requireActivity()
)
}
} }
override fun onFavoriteToggled() { override fun onFavoriteToggled() {

View file

@ -28,9 +28,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat import androidx.core.view.*
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -65,8 +63,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_player), class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_player),
MusicProgressViewUpdateHelper.Callback, MusicProgressViewUpdateHelper.Callback, PopupMenu.OnMenuItemClickListener {
View.OnLayoutChangeListener, PopupMenu.OnMenuItemClickListener {
private var lastColor: Int = 0 private var lastColor: Int = 0
private var lastPlaybackControlsColor: Int = 0 private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0 private var lastDisabledPlaybackControlsColor: Int = 0
@ -89,8 +86,8 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
binding.playerQueueSheet.updatePadding( binding.playerQueueSheet.updatePadding(
top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt() top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt()
) )
binding.recyclerView.updatePadding( binding.container.updatePadding(
top = ((1 - slideOffset) * navBarHeight).toInt() bottom = ((1 - slideOffset) * navBarHeight).toInt()
) )
} }
@ -129,9 +126,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
} }
private fun setupPanel() { private fun setupPanel() {
if (!ViewCompat.isLaidOut(binding.colorBackground) || binding.colorBackground.isLayoutRequested) { binding.colorBackground.doOnLayout {
binding.colorBackground.addOnLayoutChangeListener(this) val panel = getQueuePanel()
return panel.peekHeight = binding.container.height
} }
} }
@ -158,9 +155,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
} }
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
(binding.container) (binding.container)
) { _: View, insets: WindowInsetsCompat -> ) { v: View, insets: WindowInsetsCompat ->
navBarHeight = insets.safeGetBottomInsets() navBarHeight = insets.safeGetBottomInsets()
binding.recyclerView.updatePadding(top = navBarHeight) v.updatePadding(bottom = navBarHeight)
insets insets
} }
binding.playbackControlsFragment.root.drawAboveSystemBars() 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() { private fun setupRecyclerView() {
playingQueueAdapter = PlayingQueueAdapter( playingQueueAdapter = PlayingQueueAdapter(
requireActivity() as AppCompatActivity, requireActivity() as AppCompatActivity,

View file

@ -2,39 +2,49 @@ package code.name.monkey.retromusic.helper
import android.content.Context import android.content.Context
import android.os.Environment import android.os.Environment
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.BuildConfig 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.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.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.* import java.io.*
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
object BackupHelper { object BackupHelper : KoinComponent {
private val repository by inject<Repository>()
private val songRepository by inject<SongRepository>()
suspend fun createBackup(context: Context, name: String) { suspend fun createBackup(context: Context, name: String) {
val backupFile = val backupFile =
File(backupRootPath + File.separator + name + APPEND_EXTENSION) File(getBackupRoot(context), name + APPEND_EXTENSION)
if (backupFile.parentFile?.exists() != true) { if (backupFile.parentFile?.exists() != true) {
backupFile.parentFile?.mkdirs() backupFile.parentFile?.mkdirs()
} }
val zipItems = mutableListOf<ZipItem>() val zipItems = mutableListOf<ZipItem>()
zipItems.addAll(getDatabaseZipItems(context)) zipItems.addAll(getPlaylistZipItems(context))
zipItems.addAll(getSettingsZipItems(context)) zipItems.addAll(getSettingsZipItems(context))
getUserImageZipItems(context)?.let { zipItems.addAll(it) } getUserImageZipItems(context)?.let { zipItems.addAll(it) }
zipItems.addAll(getCustomArtistZipItems(context)) zipItems.addAll(getCustomArtistZipItems(context))
zipItems.addAll(getQueueZipItems(context))
zipAll(zipItems, backupFile) zipAll(zipItems, backupFile)
// Clean Cache Playlist Directory
File(context.filesDir, PLAYLISTS_PATH).deleteRecursively()
} }
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) = private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
kotlin.runCatching { runCatching {
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out -> ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
for (zipItem in zipItems) { for (zipItem in zipItems) {
FileInputStream(zipItem.filePath).use { fi -> FileInputStream(zipItem.filePath).use { fi ->
@ -51,7 +61,6 @@ object BackupHelper {
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT) Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
.show() .show()
} }
throw Exception(it)
}.onSuccess { }.onSuccess {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
Toast.makeText( Toast.makeText(
@ -62,34 +71,39 @@ object BackupHelper {
.show() .show()
} }
} }
} }
private fun getDatabaseZipItems(context: Context): List<ZipItem> { private suspend fun getPlaylistZipItems(context: Context): List<ZipItem> {
return context.databaseList().filter { val playlistZipItems = mutableListOf<ZipItem>()
it.endsWith(".db") && it != queueDatabase // Cache Playlist files in App storage
}.map { val playlistFolder = File(context.filesDir, PLAYLISTS_PATH)
ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it") if (!playlistFolder.exists()) {
playlistFolder.mkdirs()
} }
} for (playlist in repository.fetchPlaylistWithSongs()) {
runCatching {
private fun getQueueZipItems(context: Context): List<ZipItem> { M3UWriter.writeIO(playlistFolder, playlist)
Log.d("RetroMusic", context.getDatabasePath(queueDatabase).absolutePath) }.onSuccess { playlistFile ->
return listOf( if (playlistFile.exists()) {
ZipItem( playlistZipItems.add(
context.getDatabasePath(queueDatabase).absolutePath, ZipItem(
"$QUEUE_PATH${File.separator}$queueDatabase" playlistFile.absolutePath,
) PLAYLISTS_PATH.child(playlistFile.name)
) )
)
}
}
}
return playlistZipItems
} }
private fun getSettingsZipItems(context: Context): List<ZipItem> { private fun getSettingsZipItems(context: Context): List<ZipItem> {
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" val sharedPrefPath = File(context.filesDir.parentFile, "shared_prefs")
return listOf( return listOf(
"${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path "${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path
"$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path "$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path
).map { ).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 -> return context.filesDir.listFiles { _, name ->
name.endsWith(".jpg") name.endsWith(".jpg")
}?.map { }?.map {
ZipItem(it.absolutePath, "$IMAGES_PATH${File.separator}${it.name}") ZipItem(it.absolutePath, IMAGES_PATH.child(it.name))
} }
} }
private fun getCustomArtistZipItems(context: Context): List<ZipItem> { private fun getCustomArtistZipItems(context: Context): List<ZipItem> {
val zipItemList = mutableListOf<ZipItem>() val zipItemList = mutableListOf<ZipItem>()
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" val sharedPrefPath = File(context.filesDir.parentFile, "shared_prefs")
zipItemList.addAll( zipItemList.addAll(
File(context.filesDir, "custom_artist_images") File(context.filesDir, "custom_artist_images")
.listFiles()?.map { .listFiles()?.map {
ZipItem( ZipItem(
it.absolutePath, 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() }?.toList() ?: listOf()
) )
File(sharedPrefPath + File.separator + "custom_artist_image.xml").let { File(sharedPrefPath, "custom_artist_image.xml").let {
if (it.exists()) { if (it.exists()) {
zipItemList.add( zipItemList.add(
ZipItem( ZipItem(
it.absolutePath, 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 return zipItemList
} }
@ -138,23 +150,19 @@ object BackupHelper {
ZipInputStream(inputStream).use { ZipInputStream(inputStream).use {
var entry = it.nextEntry var entry = it.nextEntry
while (entry != null) { while (entry != null) {
if (entry.isDatabaseEntry() && contents.contains(PLAYLISTS)) { if (entry.isPlaylistEntry() && contents.contains(PLAYLISTS)) {
restoreDatabase(context, it, entry) restorePlaylists(it, entry)
} else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) { } else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) {
restorePreferences(context, it, entry) restorePreferences(context, it, entry)
} else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) { } else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) {
restoreImages(context, it, entry) restoreImages(context, it, entry)
} else if (entry.isCustomArtistEntry() && contents.contains(CUSTOM_ARTIST_IMAGES)) {
} else if (entry.isCustomArtistImageEntry() && contents.contains( if (entry.isCustomArtistPrefEntry()) {
CUSTOM_ARTIST_IMAGES restoreCustomArtistPrefs(context, it, entry)
) } else if (entry.isCustomArtistImageEntry()) {
) { restoreCustomArtistImages(context, it, entry)
restoreCustomArtistImages(context, it, entry) }
restoreCustomArtistPrefs(context, it, entry)
} else if (entry.isQueueEntry() && contents.contains(QUEUE)) {
restoreQueueDatabase(context, it, entry)
} }
entry = it.nextEntry entry = it.nextEntry
} }
} }
@ -165,14 +173,11 @@ object BackupHelper {
} }
private fun restoreImages(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { private fun restoreImages(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
val filePath = val file = File(
context.filesDir.path + File.separator + zipEntry.getFileName() context.filesDir.path, zipEntry.getFileName()
BufferedOutputStream(FileOutputStream(filePath)).use { bos -> )
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) BufferedOutputStream(FileOutputStream(file)).use { bos ->
var read: Int zipIn.copyTo(bos)
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
@ -184,39 +189,38 @@ object BackupHelper {
file.delete() file.delete()
} }
BufferedOutputStream(FileOutputStream(file)).use { bos -> BufferedOutputStream(FileOutputStream(file)).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) zipIn.copyTo(bos)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
private fun restoreDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { private suspend fun restorePlaylists(
val filePath = zipIn: ZipInputStream,
context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName() zipEntry: ZipEntry
BufferedOutputStream(FileOutputStream(filePath)).use { bos -> ) {
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) val playlistName = zipEntry.getFileName().substringBeforeLast(".")
var read: Int val songs = mutableListOf<Song>()
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
private fun restoreQueueDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { // Get songs from m3u playlist files
PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) { zipIn.bufferedReader().lineSequence().forEach { line ->
putInt("POSITION", 0) if (line.startsWith(File.separator)) {
} if (File(line).exists()) {
val filePath = songs.addAll(songRepository.songsByFilePath(line))
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)
} }
} }
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( private fun restoreCustomArtistImages(
@ -237,11 +241,7 @@ object BackupHelper {
) )
) )
).use { bos -> ).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) zipIn.copyTo(bos)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
@ -250,32 +250,30 @@ object BackupHelper {
zipIn: ZipInputStream, zipIn: ZipInputStream,
zipEntry: ZipEntry zipEntry: ZipEntry
) { ) {
val filePath = val file =
context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + zipEntry.getFileName() File(context.filesDir.parentFile, "shared_prefs".child(zipEntry.getFileName()))
BufferedOutputStream(FileOutputStream(filePath)).use { bos -> BufferedOutputStream(FileOutputStream(file)).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) zipIn.copyTo(bos)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
val backupRootPath = fun getBackupRoot(context: Context): File {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) return File(
.toString() + "/RetroMusic/Backups/" context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS),
"RetroMusic/Backups"
)
}
const val BACKUP_EXTENSION = "rmbak" const val BACKUP_EXTENSION = "rmbak"
const val APPEND_EXTENSION = ".$BACKUP_EXTENSION" const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
private const val DATABASES_PATH = "databases" private const val PLAYLISTS_PATH = "Playlists"
private const val QUEUE_PATH = "queue"
private const val SETTINGS_PATH = "prefs" private const val SETTINGS_PATH = "prefs"
private const val IMAGES_PATH = "userImages" private const val IMAGES_PATH = "userImages"
private const val CUSTOM_ARTISTS_PATH = "artistImages" private const val CUSTOM_ARTISTS_PATH = "artistImages"
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]" private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
private const val queueDatabase = "music_playback_state.db"
private fun ZipEntry.isDatabaseEntry(): Boolean { private fun ZipEntry.isPlaylistEntry(): Boolean {
return name.startsWith(DATABASES_PATH) return name.startsWith(PLAYLISTS_PATH)
} }
private fun ZipEntry.isPreferenceEntry(): Boolean { private fun ZipEntry.isPreferenceEntry(): Boolean {
@ -286,6 +284,10 @@ object BackupHelper {
return name.startsWith(IMAGES_PATH) return name.startsWith(IMAGES_PATH)
} }
private fun ZipEntry.isCustomArtistEntry(): Boolean {
return name.startsWith(CUSTOM_ARTISTS_PATH)
}
private fun ZipEntry.isCustomArtistImageEntry(): Boolean { private fun ZipEntry.isCustomArtistImageEntry(): Boolean {
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("custom_artist_images") 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") return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
} }
private fun ZipEntry.isQueueEntry(): Boolean { private fun ZipEntry.getFileName(): String {
return name.startsWith(QUEUE_PATH) return name.substring(name.lastIndexOf(File.separator) + 1)
} }
private fun ZipEntry.getFileName(): String { fun getTimeStamp(): String {
return name.substring(name.lastIndexOf(File.separator)) return SimpleDateFormat("dd-MMM yyyy HHmmss", Locale.getDefault()).format(Date())
} }
} }
@ -318,10 +320,13 @@ fun CharSequence.sanitize(): String {
.replace("&", "_") .replace("&", "_")
} }
fun String.child(child: String): String {
return this + File.separator + child
}
enum class BackupContent { enum class BackupContent {
SETTINGS, SETTINGS,
USER_IMAGES, USER_IMAGES,
CUSTOM_ARTIST_IMAGES, CUSTOM_ARTIST_IMAGES,
PLAYLISTS, PLAYLISTS
QUEUE
} }

View file

@ -459,7 +459,7 @@ object MusicPlayerRemote : KoinComponent {
songFile = File(path) songFile = File(path)
} }
if (songFile == null && uri.path != null) { if (songFile == null && uri.path != null) {
songFile = File(uri.path) songFile = File(uri.path!!)
} }
if (songFile != null) { if (songFile != null) {
songs = songRepository.songsByFilePath(songFile.absolutePath) songs = songRepository.songsByFilePath(songFile.absolutePath)

View file

@ -134,7 +134,7 @@ class SortOrder {
companion object { companion object {
/* Artist song sort order A-Z */ /* 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 */ /* Artist song sort order Z-A */
const val SONG_Z_A = "$SONG_A_Z DESC" const val SONG_Z_A = "$SONG_A_Z DESC"

View file

@ -169,10 +169,10 @@ class CoverLrcView @JvmOverloads constructor(
mCurrentLine = centerLine mCurrentLine = centerLine
invalidate() invalidate()
return true return true
} else {
callOnClick()
return true
} }
} else {
callOnClick()
return true
} }
return super.onSingleTapConfirmed(e) return super.onSingleTapConfirmed(e)
} }

View file

@ -14,7 +14,9 @@
package code.name.monkey.retromusic.model 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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
data class Artist( data class Artist(
val id: Long, val id: Long,
@ -29,7 +31,7 @@ data class Artist(
name = artistName name = artistName
} }
var name: String = "" var name: String = "-"
get() { get() {
val name = if (isAlbumArtist) getAlbumArtistName() val name = if (isAlbumArtist) getAlbumArtistName()
else getArtistName() else getArtistName()
@ -57,6 +59,39 @@ data class Artist(
val songs: List<Song> val songs: List<Song>
get() = albums.flatMap { it.songs } get() = albums.flatMap { it.songs }
val sortedSongs: List<Song>
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 { fun safeGetFirstAlbum(): Album {
return albums.firstOrNull() ?: Album.empty return albums.firstOrNull() ?: Album.empty
} }

View file

@ -49,6 +49,4 @@ open class Playlist(
result = 31 * result + name.hashCode() result = 31 * result + name.hashCode()
return result return result
} }
} }

View file

@ -55,7 +55,6 @@ class LibraryPreferenceDialog : DialogFragment() {
.inflate(R.layout.preference_dialog_library_categories, null) .inflate(R.layout.preference_dialog_library_categories, null)
val categoryAdapter = CategoryInfoAdapter() val categoryAdapter = CategoryInfoAdapter()
categoryAdapter.categoryInfos = PreferenceUtil.libraryCategory
val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view) val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(activity) recyclerView.layoutManager = LinearLayoutManager(activity)
recyclerView.adapter = categoryAdapter recyclerView.adapter = categoryAdapter

View file

@ -17,7 +17,6 @@ package code.name.monkey.retromusic.repository
import android.content.ContentResolver import android.content.ContentResolver
import android.database.Cursor import android.database.Cursor
import android.provider.BaseColumns import android.provider.BaseColumns
import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
import android.provider.MediaStore.Audio.Playlists.* import android.provider.MediaStore.Audio.Playlists.*
import android.provider.MediaStore.Audio.PlaylistsColumns import android.provider.MediaStore.Audio.PlaylistsColumns
@ -120,13 +119,18 @@ class RealPlaylistRepository(
private fun getPlaylistFromCursorImpl( private fun getPlaylistFromCursorImpl(
cursor: Cursor cursor: Cursor
): Playlist { ): Playlist {
val id = cursor.getLong(MediaStore.MediaColumns._ID) val id = cursor.getLong(0)
val name = cursor.getString(NAME) val name = cursor.getString(1)
return Playlist(id, name) return if (name != null) {
Playlist(id, name)
} else {
Playlist.empty
}
} }
override fun playlistSongs(playlistId: Long): List<Song> { override fun playlistSongs(playlistId: Long): List<Song> {
val songs = arrayListOf<Song>() val songs = arrayListOf<Song>()
if (playlistId == -1L) return songs
val cursor = makePlaylistSongCursor(playlistId) val cursor = makePlaylistSongCursor(playlistId)
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {

View file

@ -130,6 +130,13 @@ object PreferenceUtil {
) )
set(value) = sharedPreferences.edit { putString(ALBUM_DETAIL_SONG_SORT_ORDER, value) } 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 var songSortOrder
get() = sharedPreferences.getStringOrDefault( get() = sharedPreferences.getStringOrDefault(
SONG_SORT_ORDER, SONG_SORT_ORDER,

View file

@ -181,6 +181,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="8" android:layout_weight="8"
android:maxHeight="3dp" android:maxHeight="3dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
android:progressTint="@color/md_white_1000" android:progressTint="@color/md_white_1000"
android:splitTrack="false" android:splitTrack="false"

View file

@ -181,6 +181,7 @@
android:id="@+id/progressSlider" android:id="@+id/progressSlider"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingVertical="@dimen/seekbar_padding"
app:layout_constraintBottom_toTopOf="@id/songInfo" app:layout_constraintBottom_toTopOf="@id/songInfo"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -188,6 +188,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="8" android:layout_weight="8"
android:maxHeight="3dp" android:maxHeight="3dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
android:progressTint="@color/md_white_1000" android:progressTint="@color/md_white_1000"
android:splitTrack="false" android:splitTrack="false"

View file

@ -38,14 +38,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="48dp" android:minHeight="48dp"
android:text="@string/databases_description" /> android:text="@string/playlists" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/check_queue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:text="@string/now_playing_queue" />
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/check_user_images" android:id="@+id/check_user_images"

View file

@ -19,6 +19,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxHeight="3dp" android:maxHeight="3dp"
android:paddingVertical="@dimen/seekbar_padding"
android:splitTrack="false" android:splitTrack="false"
tools:progress="20" /> tools:progress="20" />

View file

@ -27,6 +27,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<code.name.monkey.retromusic.views.insets.InsetsConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -79,9 +79,22 @@
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textStyle="bold" android:textStyle="bold"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@id/song_sort_order"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/albumRecyclerView" /> app:layout_constraintTop_toBottomOf="@id/albumRecyclerView"/>
<ImageButton
android:id="@+id/song_sort_order"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:background="@null"
android:padding="8dp"
android:src="@drawable/ic_sort"
app:layout_constraintBottom_toBottomOf="@+id/songTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/songTitle"
app:layout_constraintTop_toTopOf="@id/songTitle" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
@ -193,4 +206,4 @@
android:layout_height="72dp" android:layout_height="72dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/listeners" /> app:layout_constraintTop_toBottomOf="@id/listeners" />
</code.name.monkey.retromusic.views.insets.InsetsConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -41,6 +41,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
android:splitTrack="false" android:splitTrack="false"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"

View file

@ -53,6 +53,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:paddingStart="20dp" android:paddingStart="20dp"
android:paddingEnd="20dp" android:paddingEnd="20dp"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"

View file

@ -28,10 +28,11 @@
<androidx.appcompat.widget.AppCompatSeekBar <androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/progressSlider" android:id="@+id/progressSlider"
android:layout_width="0dp"
style="@style/MusicProgressSlider" style="@style/MusicProgressSlider"
android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1"/> android:layout_weight="1"
android:paddingVertical="@dimen/seekbar_padding" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/songTotalTime" android:id="@+id/songTotalTime"

View file

@ -172,6 +172,7 @@
android:id="@+id/progressSlider" android:id="@+id/progressSlider"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingVertical="@dimen/seekbar_padding"
app:layout_constraintBottom_toTopOf="@+id/songInfo" app:layout_constraintBottom_toTopOf="@+id/songInfo"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -50,6 +50,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_toLeftOf="@id/songTotalTime" android:layout_toLeftOf="@id/songTotalTime"
android:layout_toRightOf="@id/songCurrentProgress" android:layout_toRightOf="@id/songCurrentProgress"
android:paddingVertical="@dimen/seekbar_padding"
tools:ignore="RtlHardcoded,UnusedAttribute" /> tools:ignore="RtlHardcoded,UnusedAttribute" />
</RelativeLayout> </RelativeLayout>

View file

@ -28,6 +28,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -37,6 +37,7 @@
style="@style/MusicProgressSlider" style="@style/MusicProgressSlider"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingVertical="@dimen/seekbar_padding"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"
tools:ignore="RtlHardcoded,UnusedAttribute" /> tools:ignore="RtlHardcoded,UnusedAttribute" />

View file

@ -27,6 +27,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="8" android:layout_weight="8"
android:paddingVertical="@dimen/seekbar_padding"
android:splitTrack="false" android:splitTrack="false"
android:thumb="@drawable/switch_square" android:thumb="@drawable/switch_square"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"

View file

@ -99,6 +99,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -101,6 +101,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:paddingVertical="@dimen/seekbar_padding"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"
app:layout_constraintTop_toBottomOf="@id/titleContainer" app:layout_constraintTop_toBottomOf="@id/titleContainer"

View file

@ -30,6 +30,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -29,6 +29,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -40,6 +40,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -33,6 +33,7 @@
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlHardcoded,UnusedAttribute" tools:ignore="RtlHardcoded,UnusedAttribute"
android:paddingVertical="@dimen/seekbar_padding"
tools:progress="20" /> tools:progress="20" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView

View file

@ -27,6 +27,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime" app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress" app:layout_constraintStart_toEndOf="@id/songCurrentProgress"

View file

@ -29,6 +29,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:maxHeight="2dp" android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek" android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintBottom_toBottomOf="@+id/volumeDown" app:layout_constraintBottom_toBottomOf="@+id/volumeDown"
app:layout_constraintEnd_toStartOf="@+id/volumeUp" app:layout_constraintEnd_toStartOf="@+id/volumeUp"

View file

@ -20,13 +20,14 @@
android:layout_height="40dp" android:layout_height="40dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:padding="8dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/ic_restore"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/title" app:layout_constraintEnd_toStartOf="@id/title"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="0dp" android:layout_width="0dp"

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/action_sort_order_title"
android:title="@string/sort_order_a_z" />
<item
android:id="@+id/action_sort_order_title_desc"
android:title="@string/sort_order_z_a" />
<item
android:id="@+id/action_sort_order_album"
android:title="@string/sort_order_album" />
<item
android:id="@+id/action_sort_order_year"
android:title="@string/sort_order_year" />
<item
android:id="@+id/action_sort_order_song_duration"
android:title="@string/song_duration" />
</group>
</menu>

View file

@ -146,7 +146,6 @@
<string name="currently_listening_to_x_by_x">يستمع حالياً إلى %1$s لـ %2$s</string> <string name="currently_listening_to_x_by_x">يستمع حالياً إلى %1$s لـ %2$s</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">أسود قليلاً</string> <string name="dark_theme_name">أسود قليلاً</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">حذف قائمة التشغيل</string> <string name="delete_playlist_title">حذف قائمة التشغيل</string>
<string name="delete_playlist_x"><![CDATA[حذف قائمة التشغيل <b>%1$s</b>؟]]></string> <string name="delete_playlist_x"><![CDATA[حذف قائمة التشغيل <b>%1$s</b>؟]]></string>
<string name="delete_playlists_title">حذف قوائم التشغيل</string> <string name="delete_playlists_title">حذف قوائم التشغيل</string>

View file

@ -140,7 +140,6 @@
<string name="currently_listening_to_x_by_x">Aktuálně posloucháš %1$s od %2$s.</string> <string name="currently_listening_to_x_by_x">Aktuálně posloucháš %1$s od %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Tmavá</string> <string name="dark_theme_name">Tmavá</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Smazat seznam skladeb</string> <string name="delete_playlist_title">Smazat seznam skladeb</string>
<string name="delete_playlist_x"><![CDATA[Smazat seznam skladeb <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Smazat seznam skladeb <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Smazat seznamy skladeb</string> <string name="delete_playlists_title">Smazat seznamy skladeb</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Spielt zur Zeit %1$s von %2$s.</string> <string name="currently_listening_to_x_by_x">Spielt zur Zeit %1$s von %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Dunkel</string> <string name="dark_theme_name">Dunkel</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Playlist löschen</string> <string name="delete_playlist_title">Playlist löschen</string>
<string name="delete_playlist_x"><![CDATA[Playlist <b>%1$s</b> löschen?]]></string> <string name="delete_playlist_x"><![CDATA[Playlist <b>%1$s</b> löschen?]]></string>
<string name="delete_playlists_title">Playlisten löschen</string> <string name="delete_playlists_title">Playlisten löschen</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Παίζει το %1$s από τους %2$s.</string> <string name="currently_listening_to_x_by_x">Παίζει το %1$s από τους %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Κάπως Σκούρο</string> <string name="dark_theme_name">Κάπως Σκούρο</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Διαγραφή playlist</string> <string name="delete_playlist_title">Διαγραφή playlist</string>
<string name="delete_playlist_x"><![CDATA[Διαγραφή της λίστα αναπαραγωγής <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Διαγραφή της λίστα αναπαραγωγής <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Διαγραφή αυτών των λιστών αναπαραγωγής</string> <string name="delete_playlists_title">Διαγραφή αυτών των λιστών αναπαραγωγής</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Actualmente estás escuchando %1$s de %2$s.</string> <string name="currently_listening_to_x_by_x">Actualmente estás escuchando %1$s de %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Un poco Oscuro</string> <string name="dark_theme_name">Un poco Oscuro</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Eliminar lista de reproducción</string> <string name="delete_playlist_title">Eliminar lista de reproducción</string>
<string name="delete_playlist_x"><![CDATA[¿Eliminar la lista de reproducción <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[¿Eliminar la lista de reproducción <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Eliminar listas de reproducción</string> <string name="delete_playlists_title">Eliminar listas de reproducción</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">در حال حاضر شما به s$1% از s$2% گوش می‌دهید.</string> <string name="currently_listening_to_x_by_x">در حال حاضر شما به s$1% از s$2% گوش می‌دهید.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">تقریبا مشکی</string> <string name="dark_theme_name">تقریبا مشکی</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">حذف کردن پلی لیست</string> <string name="delete_playlist_title">حذف کردن پلی لیست</string>
<string name="delete_playlist_x"><![CDATA[حذف پلی لیست <b>%1$s</b>?<b>?]]></string> <string name="delete_playlist_x"><![CDATA[حذف پلی لیست <b>%1$s</b>?<b>?]]></string>
<string name="delete_playlists_title">حذف کردن پلی لیست ها</string> <string name="delete_playlists_title">حذف کردن پلی لیست ها</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Kasalukuyang nakikinig sa %1$s ni %2$s.</string> <string name="currently_listening_to_x_by_x">Kasalukuyang nakikinig sa %1$s ni %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Medyo Madilim</string> <string name="dark_theme_name">Medyo Madilim</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Tanggalin ang playlist</string> <string name="delete_playlist_title">Tanggalin ang playlist</string>
<string name="delete_playlist_x"><![CDATA[Tanggalin ang playlist na <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Tanggalin ang playlist na <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Tanggalin ang mga playlist</string> <string name="delete_playlists_title">Tanggalin ang mga playlist</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Vous écoutez actuellement %1$s par %2$s.</string> <string name="currently_listening_to_x_by_x">Vous écoutez actuellement %1$s par %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Plutôt Sombre</string> <string name="dark_theme_name">Plutôt Sombre</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Supprimer la liste de lecture</string> <string name="delete_playlist_title">Supprimer la liste de lecture</string>
<string name="delete_playlist_x"><![CDATA[Supprimer la liste <b>%1$s</b> ?]]></string> <string name="delete_playlist_x"><![CDATA[Supprimer la liste <b>%1$s</b> ?]]></string>
<string name="delete_playlists_title">Supprimer les listes de lecture</string> <string name="delete_playlists_title">Supprimer les listes de lecture</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Delete playlists</string> <string name="delete_playlists_title">Delete playlists</string>

View file

@ -138,7 +138,6 @@
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Delete playlists</string> <string name="delete_playlists_title">Delete playlists</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Jelenleg %1$s hallgatása %2$s által.</string> <string name="currently_listening_to_x_by_x">Jelenleg %1$s hallgatása %2$s által.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kissé sötét</string> <string name="dark_theme_name">Kissé sötét</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Lejátszási lista törlése</string> <string name="delete_playlist_title">Lejátszási lista törlése</string>
<string name="delete_playlist_x"><![CDATA[Törli a <b>%1$s</b> lejátszási listát?]]></string> <string name="delete_playlist_x"><![CDATA[Törli a <b>%1$s</b> lejátszási listát?]]></string>
<string name="delete_playlists_title">Lejátszási listák törlése</string> <string name="delete_playlists_title">Lejátszási listák törlése</string>

View file

@ -137,7 +137,6 @@ https://play.google.com/store/apps/details?id=%s</string>
<string name="currently_listening_to_x_by_x">Ascoltando attualmente %1$s di %2$s.</string> <string name="currently_listening_to_x_by_x">Ascoltando attualmente %1$s di %2$s.</string>
<string name="custom_artist_images">Immagini dell\'Artista Personalizzate</string> <string name="custom_artist_images">Immagini dell\'Artista Personalizzate</string>
<string name="dark_theme_name">Scuro</string> <string name="dark_theme_name">Scuro</string>
<string name="databases_description">Database (Playlist, Cronologia, Più riprodotti, ecc.)</string>
<string name="delete_playlist_title">Elimina playlist</string> <string name="delete_playlist_title">Elimina playlist</string>
<string name="delete_playlist_x"><![CDATA[Elimina la playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Elimina la playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Elimina playlist</string> <string name="delete_playlists_title">Elimina playlist</string>

View file

@ -135,7 +135,6 @@
<string name="currently_listening_to_x_by_x">現在、 %1$s によって %2$s で聴いています。</string> <string name="currently_listening_to_x_by_x">現在、 %1$s によって %2$s で聴いています。</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">ダーク</string> <string name="dark_theme_name">ダーク</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">プレイリストを削除</string> <string name="delete_playlist_title">プレイリストを削除</string>
<string name="delete_playlist_x"><![CDATA[プレイリスト <b>%1$s</b> を削除しますか?]]></string> <string name="delete_playlist_x"><![CDATA[プレイリスト <b>%1$s</b> を削除しますか?]]></string>
<string name="delete_playlists_title">プレイリストを削除</string> <string name="delete_playlists_title">プレイリストを削除</string>

View file

@ -134,7 +134,6 @@
<string name="currently_listening_to_x_by_x">현재 %2$s의 %1$s를 듣는 중입니다.</string> <string name="currently_listening_to_x_by_x">현재 %2$s의 %1$s를 듣는 중입니다.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">카인다 다크</string> <string name="dark_theme_name">카인다 다크</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">재생목록 삭제</string> <string name="delete_playlist_title">재생목록 삭제</string>
<string name="delete_playlist_x"><![CDATA[<b>%1$s</b> 재생목록을 삭제하시겠습니까?]]></string> <string name="delete_playlist_x"><![CDATA[<b>%1$s</b> 재생목록을 삭제하시겠습니까?]]></string>
<string name="delete_playlists_title">재생목록 삭제</string> <string name="delete_playlists_title">재생목록 삭제</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Delete playlists</string> <string name="delete_playlists_title">Delete playlists</string>

View file

@ -134,7 +134,6 @@
<string name="currently_listening_to_x_by_x">%2$s သီဆိုထားသော %1$s ကိုယခုနားထောင်နေသည်</string> <string name="currently_listening_to_x_by_x">%2$s သီဆိုထားသော %1$s ကိုယခုနားထောင်နေသည်</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">အနက်ရောင်ဆန်ဆန်</string> <string name="dark_theme_name">အနက်ရောင်ဆန်ဆန်</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Playlist ဖျက်ခြင်း</string> <string name="delete_playlist_title">Playlist ဖျက်ခြင်း</string>
<string name="delete_playlist_x"><![CDATA[<b>%1$s</b> Playlist ကိုဖျက်မှာလား]]></string> <string name="delete_playlist_x"><![CDATA[<b>%1$s</b> Playlist ကိုဖျက်မှာလား]]></string>
<string name="delete_playlists_title">Playlist များဖျက်ခြင်း</string> <string name="delete_playlists_title">Playlist များဖျက်ခြင်း</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Nu luisterend naar %1$s van %2$s.</string> <string name="currently_listening_to_x_by_x">Nu luisterend naar %1$s van %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Soort van donker</string> <string name="dark_theme_name">Soort van donker</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Afspeellijst verwijderen</string> <string name="delete_playlist_title">Afspeellijst verwijderen</string>
<string name="delete_playlist_x"><![CDATA[Afspellijst <b>%1$s</b> verwijderen?]]></string> <string name="delete_playlist_x"><![CDATA[Afspellijst <b>%1$s</b> verwijderen?]]></string>
<string name="delete_playlists_title">Afspeellijsten verwijderen</string> <string name="delete_playlists_title">Afspeellijsten verwijderen</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Delete playlists</string> <string name="delete_playlists_title">Delete playlists</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Delete playlists</string> <string name="delete_playlists_title">Delete playlists</string>

View file

@ -140,7 +140,6 @@
<string name="currently_listening_to_x_by_x">Aktualnie odtwarzane %1$s wykonawcy %2$s.</string> <string name="currently_listening_to_x_by_x">Aktualnie odtwarzane %1$s wykonawcy %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Dość ciemny</string> <string name="dark_theme_name">Dość ciemny</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Usuń playlistę</string> <string name="delete_playlist_title">Usuń playlistę</string>
<string name="delete_playlist_x"><![CDATA[Usunąć playlistę <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Usunąć playlistę <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Usuń playlisty</string> <string name="delete_playlists_title">Usuń playlisty</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Atualmente ouvindo %1$s por %2$s.</string> <string name="currently_listening_to_x_by_x">Atualmente ouvindo %1$s por %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Meio escuro</string> <string name="dark_theme_name">Meio escuro</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Excluir playlist</string> <string name="delete_playlist_title">Excluir playlist</string>
<string name="delete_playlist_x"><![CDATA[Excluir a playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Excluir a playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Excluir playlists</string> <string name="delete_playlists_title">Excluir playlists</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">A ouvir %1$s de %2$s.</string> <string name="currently_listening_to_x_by_x">A ouvir %1$s de %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Meio escuro</string> <string name="dark_theme_name">Meio escuro</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Eliminar lista</string> <string name="delete_playlist_title">Eliminar lista</string>
<string name="delete_playlist_x"><![CDATA[Eliminar a lista <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Eliminar a lista <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Eliminar listas</string> <string name="delete_playlists_title">Eliminar listas</string>

View file

@ -138,7 +138,6 @@
<string name="currently_listening_to_x_by_x">Acum ascultați %1$s de %2$s.</string> <string name="currently_listening_to_x_by_x">Acum ascultați %1$s de %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Suriu</string> <string name="dark_theme_name">Suriu</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Șterge playlist</string> <string name="delete_playlist_title">Șterge playlist</string>
<string name="delete_playlist_x"><![CDATA[Doriți să ștergeți playlist-ul <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Doriți să ștergeți playlist-ul <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Șterge playlist-uri</string> <string name="delete_playlists_title">Șterge playlist-uri</string>

View file

@ -140,7 +140,6 @@
<string name="currently_listening_to_x_by_x">Сейчас играет %1$s от %2$s.</string> <string name="currently_listening_to_x_by_x">Сейчас играет %1$s от %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Тёмная</string> <string name="dark_theme_name">Тёмная</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Удалить плейлист</string> <string name="delete_playlist_title">Удалить плейлист</string>
<string name="delete_playlist_x"><![CDATA[Удалить плейлист <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Удалить плейлист <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Удалить плейлисты</string> <string name="delete_playlists_title">Удалить плейлисты</string>

View file

@ -138,7 +138,6 @@
<string name="currently_listening_to_x_by_x">Trenutno se reprodukuje %1$s izvodjaca %2$s</string> <string name="currently_listening_to_x_by_x">Trenutno se reprodukuje %1$s izvodjaca %2$s</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kao tamno</string> <string name="dark_theme_name">Kao tamno</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Izbrisi plejlistu</string> <string name="delete_playlist_title">Izbrisi plejlistu</string>
<string name="delete_playlist_x"><![CDATA[Izbrisi <b>%1$s</b> plejlistu?]]></string> <string name="delete_playlist_x"><![CDATA[Izbrisi <b>%1$s</b> plejlistu?]]></string>
<string name="delete_playlists_title">Izbrisi plejlistu</string> <string name="delete_playlists_title">Izbrisi plejlistu</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Lyssnar just nu på %1$s av %2$s.</string> <string name="currently_listening_to_x_by_x">Lyssnar just nu på %1$s av %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Ganska mörkt</string> <string name="dark_theme_name">Ganska mörkt</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Radera spellista</string> <string name="delete_playlist_title">Radera spellista</string>
<string name="delete_playlist_x"><![CDATA[Radera spellistan <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Radera spellistan <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Radera spellistor</string> <string name="delete_playlists_title">Radera spellistor</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Delete playlists</string> <string name="delete_playlists_title">Delete playlists</string>

View file

@ -134,7 +134,6 @@
<string name="currently_listening_to_x_by_x">กำลังฟัง %1$s ของ %2$s</string> <string name="currently_listening_to_x_by_x">กำลังฟัง %1$s ของ %2$s</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">ลบเพลย์ลิสต์</string> <string name="delete_playlist_title">ลบเพลย์ลิสต์</string>
<string name="delete_playlist_x"><![CDATA[ลบเพลย์ลิสต์ <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[ลบเพลย์ลิสต์ <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">ลบเพลย์ลิสต์</string> <string name="delete_playlists_title">ลบเพลย์ลิสต์</string>

View file

@ -136,7 +136,6 @@
<string name="currently_listening_to_x_by_x">Şu anda %2$s şarkıcısından %1$s dinleniyor.</string> <string name="currently_listening_to_x_by_x">Şu anda %2$s şarkıcısından %1$s dinleniyor.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Koyu</string> <string name="dark_theme_name">Koyu</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Çalma listesini sil</string> <string name="delete_playlist_title">Çalma listesini sil</string>
<string name="delete_playlist_x"><![CDATA[Oynatma listesi <b>%1$s</b> silinsin mi?]]></string> <string name="delete_playlist_x"><![CDATA[Oynatma listesi <b>%1$s</b> silinsin mi?]]></string>
<string name="delete_playlists_title">Çalma listelerini sil</string> <string name="delete_playlists_title">Çalma listelerini sil</string>

View file

@ -140,7 +140,6 @@
<string name="currently_listening_to_x_by_x">Зараз грає %1$s від %2$s.</string> <string name="currently_listening_to_x_by_x">Зараз грає %1$s від %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Майже темна</string> <string name="dark_theme_name">Майже темна</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Видалити список відтворення</string> <string name="delete_playlist_title">Видалити список відтворення</string>
<string name="delete_playlist_x"><![CDATA[Видалити список відтворення <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Видалити список відтворення <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Видалити списки відтворення</string> <string name="delete_playlists_title">Видалити списки відтворення</string>

View file

@ -135,7 +135,6 @@ tiếp tục xảy ra hãy \"Xóa dữ liệu ứng dụng\"</string>
<string name="currently_listening_to_x_by_x">Đang nghe %1$s bởi %2$s.</string> <string name="currently_listening_to_x_by_x">Đang nghe %1$s bởi %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Xám đen</string> <string name="dark_theme_name">Xám đen</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Xoá danh sách phát</string> <string name="delete_playlist_title">Xoá danh sách phát</string>
<string name="delete_playlist_x"><![CDATA[Xoá danh sách phát <b>%1$s</b>?]]></string> <string name="delete_playlist_x"><![CDATA[Xoá danh sách phát <b>%1$s</b>?]]></string>
<string name="delete_playlists_title">Xoá danh sách phát</string> <string name="delete_playlists_title">Xoá danh sách phát</string>

View file

@ -134,7 +134,6 @@
<string name="currently_listening_to_x_by_x">正在收听 %2$s 的 %1$s。</string> <string name="currently_listening_to_x_by_x">正在收听 %2$s 的 %1$s。</string>
<string name="custom_artist_images">自定义艺术家图像</string> <string name="custom_artist_images">自定义艺术家图像</string>
<string name="dark_theme_name">深色</string> <string name="dark_theme_name">深色</string>
<string name="databases_description">数据库(播放列表、播放历史、最多播放等)</string>
<string name="delete_playlist_title">删除播放列表</string> <string name="delete_playlist_title">删除播放列表</string>
<string name="delete_playlist_x"><![CDATA[确定删除播放列表 <b>%1$s</b> 吗?]]></string> <string name="delete_playlist_x"><![CDATA[确定删除播放列表 <b>%1$s</b> 吗?]]></string>
<string name="delete_playlists_title">删除播放列表</string> <string name="delete_playlists_title">删除播放列表</string>

View file

@ -134,7 +134,6 @@
<string name="currently_listening_to_x_by_x">我正在聽 %2$s 的 %1$s</string> <string name="currently_listening_to_x_by_x">我正在聽 %2$s 的 %1$s</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">暗沉</string> <string name="dark_theme_name">暗沉</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">刪除播放清單</string> <string name="delete_playlist_title">刪除播放清單</string>
<string name="delete_playlist_x"><![CDATA[確定要刪除播放清單 <b>%1$s</b> 嗎?]]></string> <string name="delete_playlist_x"><![CDATA[確定要刪除播放清單 <b>%1$s</b> 嗎?]]></string>
<string name="delete_playlists_title">刪除多個播放清單</string> <string name="delete_playlists_title">刪除多個播放清單</string>

View file

@ -64,4 +64,6 @@
<dimen name="m3_card_medium_radius">24dp</dimen> <dimen name="m3_card_medium_radius">24dp</dimen>
<dimen name="m3_card_large_radius">40dp</dimen> <dimen name="m3_card_large_radius">40dp</dimen>
<dimen name="padding_album_cover">16dp</dimen> <dimen name="padding_album_cover">16dp</dimen>
<dimen name="seekbar_padding">16dp</dimen>
</resources> </resources>

View file

@ -138,7 +138,6 @@
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"> <string name="delete_playlist_x">
<![CDATA[Delete the playlist <b>%1$s</b>?]]> <![CDATA[Delete the playlist <b>%1$s</b>?]]>