This commit is contained in:
Hemanth S 2022-01-05 12:11:04 +05:30
commit 3a6645ab35
116 changed files with 861 additions and 651 deletions

View file

@ -14,7 +14,7 @@ android {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic" applicationId "code.name.monkey.retromusic"
versionCode 10556 versionCode 10557
versionName '5.6.1' versionName '5.6.1'
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"") buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
@ -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
} }
@ -86,7 +88,6 @@ static def getDate() {
new Date().format('MMddyyyyss') new Date().format('MMddyyyyss')
} }
dependencies { dependencies {
implementation project(':appthemehelper') implementation project(':appthemehelper')
implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.gridlayout:gridlayout:1.0.0"
@ -149,6 +150,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

@ -73,7 +73,7 @@ const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
const val CAROUSEL_EFFECT = "carousel_effect" const val CAROUSEL_EFFECT = "carousel_effect"
const val COLORED_NOTIFICATION = "colored_notification" const val COLORED_NOTIFICATION = "colored_notification"
const val CLASSIC_NOTIFICATION = "classic_notification" const val CLASSIC_NOTIFICATION = "classic_notification"
const val GAP_LESS_PLAYBACK = "gap_less_playback" const val GAP_LESS_PLAYBACK = "gapless_playback"
const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen" const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen"
const val BLURRED_ALBUM_ART = "blurred_album_art" const val BLURRED_ALBUM_ART = "blurred_album_art"
const val NEW_BLUR_AMOUNT = "new_blur_amount" const val NEW_BLUR_AMOUNT = "new_blur_amount"
@ -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"
@ -153,3 +154,4 @@ const val LAST_USED_TAB = "last_used_tab"
const val WHITELIST_MUSIC = "whitelist_music" const val WHITELIST_MUSIC = "whitelist_music"
const val MATERIAL_YOU = "material_you" const val MATERIAL_YOU = "material_you"
const val SNOWFALL = "snowfall" const val SNOWFALL = "snowfall"
const val LYRICS_TYPE = "lyrics_type"

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

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.widget.Toast import android.widget.Toast
import androidx.core.animation.doOnEnd
import androidx.lifecycle.* import androidx.lifecycle.*
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.db.* import code.name.monkey.retromusic.db.*
@ -255,11 +256,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)
} }
@ -365,7 +368,7 @@ class LibraryViewModel(
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
Toast.makeText( Toast.makeText(
App.getContext(), App.getContext(),
"Adding songs to $playlistName", "Adding songs to $playlistName",
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
@ -378,17 +381,21 @@ class LibraryViewModel(
} }
fun setFabMargin(bottomMargin: Int) { fun setFabMargin(bottomMargin: Int) {
println("Bottom Margin $bottomMargin")
val currentValue = DensityUtil.dip2px(App.getContext(), 16F) + val currentValue = DensityUtil.dip2px(App.getContext(), 16F) +
bottomMargin bottomMargin
if (currentValue != fabMargin.value) { ValueAnimator.ofInt(fabMargin.value!!, currentValue).apply {
ValueAnimator.ofInt(fabMargin.value!!, currentValue).apply { addUpdateListener {
addUpdateListener { fabMargin.postValue(
fabMargin.postValue( (it.animatedValue as Int).also { bottomMarginAnimated ->
it.animatedValue as Int println("Bottom Margin Animated $bottomMarginAnimated")
) }
} )
start()
} }
doOnEnd {
fabMargin.postValue(currentValue)
}
start()
} }
} }
} }

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

@ -39,6 +39,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.navOptions import androidx.navigation.navOptions
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.EXTRA_ARTIST_ID
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
@ -237,7 +238,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
val isFavorite: Boolean = val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id) libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Main) { withContext(Main) {
val icon = if (animate) { val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else { } else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border

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,17 +14,19 @@
*/ */
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
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.LYRICS_TYPE
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.SHOW_LYRICS import code.name.monkey.retromusic.SHOW_LYRICS
import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter
@ -34,7 +36,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
@ -43,6 +44,7 @@ import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.transform.CarousalPagerTransformer import code.name.monkey.retromusic.transform.CarousalPagerTransformer
import code.name.monkey.retromusic.transform.ParallaxPagerTransformer import code.name.monkey.retromusic.transform.ParallaxPagerTransformer
import code.name.monkey.retromusic.util.LyricUtil import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.LyricsType
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -201,6 +203,8 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
showLyrics(false) showLyrics(false)
progressViewUpdateHelper?.stop() progressViewUpdateHelper?.stop()
} }
} else if (key == LYRICS_TYPE) {
maybeInitLyrics()
} }
} }
@ -223,8 +227,21 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} }
private fun showLyrics(visible: Boolean) { private fun showLyrics(visible: Boolean) {
lrcView.isVisible = visible binding.coverLyrics.isVisible = false
viewPager.isInvisible = visible binding.lyricsView.isVisible = false
binding.viewPager.isVisible = true
val lyrics: View = if (PreferenceUtil.lyricsType == LyricsType.REPLACE_LYRICS) {
ObjectAnimator.ofFloat(viewPager, View.ALPHA, if (visible) 0F else 1F).start()
lrcView
} else {
binding.coverLyrics
}
ObjectAnimator.ofFloat(lyrics, View.ALPHA, if (visible) 1F else 0F).apply {
doOnEnd {
lyrics.isVisible = visible
}
start()
}
} }
private fun maybeInitLyrics() { private fun maybeInitLyrics() {
@ -232,9 +249,9 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
// Don't show lyrics container for below conditions // Don't show lyrics container for below conditions
if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) { if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) {
showLyrics(true) showLyrics(true)
progressViewUpdateHelper?.start() if (PreferenceUtil.lyricsType == LyricsType.REPLACE_LYRICS) {
lrcView.animate().alpha(1f).duration = progressViewUpdateHelper?.start()
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

@ -30,6 +30,7 @@ import android.widget.SeekBar
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
@ -320,7 +321,7 @@ class FullPlaybackControlsFragment :
val isFavorite: Boolean = val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id) libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val icon = if (animate) { val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else { } else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border

View file

@ -35,6 +35,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
@ -89,8 +90,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()
) )
} }
@ -158,9 +159,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()
@ -282,7 +283,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
val isFavorite: Boolean = val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id) libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val icon = if (animate) { val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else { } else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
@ -453,7 +454,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private fun updateLabel() { private fun updateLabel() {
(MusicPlayerRemote.playingQueue.size - 1).apply { (MusicPlayerRemote.playingQueue.size - 1).apply {
if (this == (MusicPlayerRemote.position)) { if (this == (MusicPlayerRemote.position)) {
binding.nextSong.text = "Last song" binding.nextSong.text = context?.resources?.getString(R.string.last_song)
} else { } else {
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
binding.nextSong.text = title binding.nextSong.text = title
@ -473,7 +474,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
oldBottom: Int oldBottom: Int
) { ) {
val panel = getQueuePanel() val panel = getQueuePanel()
panel.peekHeight = binding.container.height + navBarHeight if (panel.state == STATE_COLLAPSED) {
panel.peekHeight = binding.container.height
} else if (panel.state == STATE_EXPANDED) {
panel.peekHeight = binding.container.height + navBarHeight
}
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {

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

@ -167,12 +167,12 @@ class CoverLrcView @JvmOverloads constructor(
isShowTimeline = false isShowTimeline = false
removeCallbacks(hideTimelineRunnable) removeCallbacks(hideTimelineRunnable)
mCurrentLine = centerLine mCurrentLine = centerLine
invalidate() animateCurrentTextSize()
return true
} else {
callOnClick()
return true return true
} }
} else {
callOnClick()
return true
} }
return super.onSingleTapConfirmed(e) return super.onSingleTapConfirmed(e)
} }
@ -458,6 +458,7 @@ class CoverLrcView @JvmOverloads constructor(
mCurrentLine = line mCurrentLine = line
if (!isShowTimeline) { if (!isShowTimeline) {
smoothScrollTo(line) smoothScrollTo(line)
animateCurrentTextSize()
} else { } else {
invalidate() invalidate()
} }
@ -536,6 +537,18 @@ class CoverLrcView @JvmOverloads constructor(
canvas.restore() canvas.restore()
} }
fun animateCurrentTextSize() {
val currentTextSize = mCurrentTextSize
ValueAnimator.ofFloat(mNormalTextSize, currentTextSize).apply {
addUpdateListener {
mCurrentTextSize = it.animatedValue as Float
invalidate()
}
duration = 300L
start()
}
}
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean { override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_UP if (event.action == MotionEvent.ACTION_UP
@ -613,7 +626,7 @@ class CoverLrcView @JvmOverloads constructor(
private fun adjustCenter() { private fun adjustCenter() {
smoothScrollTo(centerLine, ADJUST_DURATION) smoothScrollTo(centerLine, ADJUST_DURATION)
} }
/** 滚动到某一行 */
/** 滚动到某一行 */ /** 滚动到某一行 */
private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) { private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) {
val offset = getOffset(line) val offset = getOffset(line)

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

@ -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

@ -31,7 +31,9 @@ import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.service.playback.Playback; import code.name.monkey.retromusic.service.playback.Playback;
import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.PreferenceUtil;
/** @author Andrew Neal, Karim Abou Zeid (kabouzeid) */ /**
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
*/
public class MultiPlayer public class MultiPlayer
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
public static final String TAG = MultiPlayer.class.getSimpleName(); public static final String TAG = MultiPlayer.class.getSimpleName();
@ -40,12 +42,14 @@ public class MultiPlayer
private MediaPlayer mNextMediaPlayer; private MediaPlayer mNextMediaPlayer;
private final Context context; private final Context context;
@Nullable @Nullable
private Playback.PlaybackCallbacks callbacks; private Playback.PlaybackCallbacks callbacks;
private boolean mIsInitialized = false; private boolean mIsInitialized = false;
/** Constructor of <code>MultiPlayer</code> */ /**
* Constructor of <code>MultiPlayer</code>
*/
MultiPlayer(final Context context) { MultiPlayer(final Context context) {
this.context = context; this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
@ -67,7 +71,7 @@ public class MultiPlayer
/** /**
* @param player The {@link MediaPlayer} to use * @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream you want to play * @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise * @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/ */
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) { private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
@ -155,13 +159,17 @@ public class MultiPlayer
this.callbacks = callbacks; this.callbacks = callbacks;
} }
/** @return True if the player is ready to go, false otherwise */ /**
* @return True if the player is ready to go, false otherwise
*/
@Override @Override
public boolean isInitialized() { public boolean isInitialized() {
return mIsInitialized; return mIsInitialized;
} }
/** Starts or resumes playback. */ /**
* Starts or resumes playback.
*/
@Override @Override
public boolean start() { public boolean start() {
try { try {
@ -172,14 +180,18 @@ public class MultiPlayer
} }
} }
/** Resets the MediaPlayer to its uninitialized state. */ /**
* Resets the MediaPlayer to its uninitialized state.
*/
@Override @Override
public void stop() { public void stop() {
mCurrentMediaPlayer.reset(); mCurrentMediaPlayer.reset();
mIsInitialized = false; mIsInitialized = false;
} }
/** Releases resources associated with this MediaPlayer object. */ /**
* Releases resources associated with this MediaPlayer object.
*/
@Override @Override
public void release() { public void release() {
stop(); stop();
@ -189,7 +201,9 @@ public class MultiPlayer
} }
} }
/** Pauses playback. Call start() to resume. */ /**
* Pauses playback. Call start() to resume.
*/
@Override @Override
public boolean pause() { public boolean pause() {
try { try {
@ -200,7 +214,9 @@ public class MultiPlayer
} }
} }
/** Checks whether the MultiPlayer is playing. */ /**
* Checks whether the MultiPlayer is playing.
*/
@Override @Override
public boolean isPlaying() { public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying(); return mIsInitialized && mCurrentMediaPlayer.isPlaying();
@ -291,7 +307,9 @@ public class MultiPlayer
return mCurrentMediaPlayer.getAudioSessionId(); return mCurrentMediaPlayer.getAudioSessionId();
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) { public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false; mIsInitialized = false;
@ -308,7 +326,9 @@ public class MultiPlayer
return false; return false;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public void onCompletion(final MediaPlayer mp) { public void onCompletion(final MediaPlayer mp) {
if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) { if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) {
@ -323,6 +343,7 @@ public class MultiPlayer
} }
} }
@Override @Override
public void setCrossFadeDuration(int duration) { } public void setCrossFadeDuration(int duration) {
}
} }

View file

@ -431,9 +431,7 @@ public class MusicService extends MediaBrowserServiceCompat
registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
setSessionToken(mediaSession.getSessionToken()); setSessionToken(mediaSession.getSessionToken());
if (VersionUtils.INSTANCE.hasMarshmallow()) { notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager = getSystemService(NotificationManager.class);
}
initNotification(); initNotification();
mediaStoreObserver = new MediaStoreObserver(this, playerHandler); mediaStoreObserver = new MediaStoreObserver(this, playerHandler);

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,
@ -660,4 +667,14 @@ object PreferenceUtil {
val isSnowFalling val isSnowFalling
get() = sharedPreferences.getBoolean(SNOWFALL, false) get() = sharedPreferences.getBoolean(SNOWFALL, false)
val lyricsType: LyricsType
get() = if (sharedPreferences.getString(LYRICS_TYPE, "0") == "0") {
LyricsType.REPLACE_LYRICS
} else {
LyricsType.OVER_LYRICS
}
} }
enum class LyricsType {
REPLACE_LYRICS, OVER_LYRICS
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

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

@ -5,7 +5,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clickable="true" android:clickable="true"
android:focusable="true"> android:focusable="true"
android:background="?attr/colorSurface">
<View <View
android:id="@+id/colorGradientBackground" android:id="@+id/colorGradientBackground"

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

@ -4,7 +4,9 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface"
android:clickable="true"
android:focusable="true">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/colorBackground" android:id="@+id/colorBackground"

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

@ -13,13 +13,20 @@
</androidx.viewpager.widget.ViewPager> </androidx.viewpager.widget.ViewPager>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/cover_lyrics"
android:name="code.name.monkey.retromusic.fragments.other.CoverLyricsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
<com.bosphere.fadingedgelayout.FadingEdgeLayout <com.bosphere.fadingedgelayout.FadingEdgeLayout
android:id="@+id/fading_edge_layout" android:id="@+id/fading_edge_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:fel_edge="top|bottom" app:fel_edge="top|bottom"
app:fel_size_bottom="80dp" app:fel_size_bottom="100dp"
app:fel_size_top="80dp"> app:fel_size_top="100dp">
<code.name.monkey.retromusic.lyrics.CoverLrcView <code.name.monkey.retromusic.lyrics.CoverLrcView
android:id="@+id/lyricsView" android:id="@+id/lyricsView"

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

@ -48,138 +48,137 @@
<string name="action_sleep_timer">Pengatur waktu tidur</string> <string name="action_sleep_timer">Pengatur waktu tidur</string>
<string name="action_sort_order">Urut berdasarkan</string> <string name="action_sort_order">Urut berdasarkan</string>
<string name="action_tag_editor">Pengubah label</string> <string name="action_tag_editor">Pengubah label</string>
<string name="action_toggle_favorite">Toggle favorite</string> <string name="action_toggle_favorite">Aktifkan/nonaktifkan favorit</string>
<string name="action_toggle_shuffle">Toggle shuffle mode</string> <string name="action_toggle_shuffle">Aktifkan/nonaktifkan mode acak</string>
<string name="adaptive">Adaptive</string> <string name="adaptive">Adaptif</string>
<string name="add_action">Add</string> <string name="add_action">Tambahkan</string>
<string name="add_playlist_title">"Tambahkan ke daftar putar"</string> <string name="add_playlist_title">"Tambahkan ke daftar putar"</string>
<string name="add_time_framed_lryics">Add Time Framed Lyrics</string> <string name="add_time_framed_lryics">Tambahkan kerangka waktu pada lirik</string>
<string name="added_title_to_playing_queue">"Added 1 title to the playing queue."</string> <string name="added_title_to_playing_queue">"1 lagu ditambahkan ke antrean."</string>
<string name="added_x_titles_to_playing_queue">Added %1$d titles to the playing queue.</string> <string name="added_x_titles_to_playing_queue">%1$d lagu telah ditambahkan ke antrean.</string>
<string name="album">Album</string> <string name="album">Album</string>
<plurals name="albumSongs"> <plurals name="albumSongs">
<item quantity="other">Songs</item> <item quantity="other">Lagu</item>
</plurals> </plurals>
<string name="album_artist">Album Artist</string> <string name="album_artist">Artis Album</string>
<string name="albums">Albums</string> <string name="albums">Album</string>
<plurals name="albums"> <plurals name="albums">
<item quantity="other">Albums</item> <item quantity="other">Album</item>
</plurals> </plurals>
<string name="always">Always</string> <string name="always">Selalu</string>
<string name="app_share">Hey check out this cool music player at: https://play.google.com/store/apps/details?id=%s</string> <string name="app_share">Hey, cobalah pemutar musik keren ini di: https://play.google.com/store/apps/details?id=%s</string>
<string name="app_shortcut_shuffle_all_short">Shuffle</string> <string name="app_shortcut_shuffle_all_short">Acak</string>
<string name="app_shortcut_top_tracks_short">Top Tracks</string> <string name="app_shortcut_top_tracks_short">Lagu Teratas</string>
<string name="app_widget_big_name">Full Image</string> <string name="app_widget_big_name">Gambar penuh</string>
<string name="app_widget_card_name">Card</string> <string name="app_widget_card_name">Kartu</string>
<string name="app_widget_classic_name">Classic</string> <string name="app_widget_classic_name">Klasik</string>
<string name="app_widget_md3_name">MD3</string> <string name="app_widget_md3_name">MD3</string>
<string name="app_widget_small_name">Small</string> <string name="app_widget_small_name">Kecil</string>
<string name="app_widget_text_name">Minimal Text</string> <string name="app_widget_text_name">Teks minimal</string>
<string name="artist">Artist</string> <string name="artist">Artis</string>
<string name="artists">Artists</string> <string name="artists">Artis</string>
<string name="audio_fade_duration">Audio Fade Duration</string> <string name="audio_fade_duration">Durasi transisi antar lagu</string>
<string name="audio_focus_denied">Audio focus denied.</string> <string name="audio_focus_denied">Fokus suara ditolak.</string>
<string name="audio_settings_summary">Change the sound settings and adjust the equalizer controls</string> <string name="audio_settings_summary">Ubah pengaturan suara dan atur equalizer</string>
<string name="auto">Auto</string> <string name="auto">Otomatis</string>
<string name="backup_restore_settings_summary">Backup and restore your settings, playlists</string> <string name="backup_restore_settings_summary">Cadangkan dan pulihkan pengaturan, daftar putar anda</string>
<string name="backup_restore_title"><![CDATA[Backup & Restore]]></string> <string name="backup_restore_title"><![CDATA[Cadangkan & Pulihkan]]></string>
<string name="backup_title">Backups</string> <string name="backup_title">Cadangkan</string>
<string name="biography">Biography</string> <string name="biography">Biografi</string>
<string name="black_theme_name">Just Black</string> <string name="black_theme_name">Hitam Pekat</string>
<string name="blacklist">Blacklist</string> <string name="blacklist">Daftar hitam</string>
<string name="blur">Blur</string> <string name="blur">Keburaman</string>
<string name="blur_card">Blur Card</string> <string name="blur_card">Kartu buram</string>
<string name="bug_report_failed">Unable to send report</string> <string name="bug_report_failed">Tidak dapat mengirim laporan</string>
<string name="bug_report_failed_invalid_token">Invalid access token. Please contact the app developer.</string> <string name="bug_report_failed_invalid_token">Token akses tidak valid. Silakan hubungi pengembang aplikasi.</string>
<string name="bug_report_failed_issues_not_available">Issues are not enabled for the selected repository. Please contact the app developer.</string> <string name="bug_report_failed_issues_not_available">Masalah tidak diaktifkan untuk repositori yang dipilih. Silakan hubungi pengembang aplikasi.</string>
<string name="bug_report_failed_unknown">An unexpected error occurred. Please contact the app developer.</string> <string name="bug_report_failed_unknown">Terjadi kesalahan yang tak terduga. Silakan hubungi pengembang aplikasi.</string>
<string name="bug_report_failed_wrong_credentials">Wrong username or password</string> <string name="bug_report_failed_wrong_credentials">Username atau password salah</string>
<string name="bug_report_issue">Issue</string> <string name="bug_report_issue">Masalah</string>
<string name="bug_report_manual">Send manually</string> <string name="bug_report_manual">Kirim secara manual</string>
<string name="bug_report_no_description">Please enter an issue description</string> <string name="bug_report_no_description">Silakan masukkan deskripsi masalah</string>
<string name="bug_report_no_password">Please enter your valid GitHub password</string> <string name="bug_report_no_password">Mohon masukkan kata sandi Github Anda dengan benar</string>
<string name="bug_report_no_title">Please enter an issue title</string> <string name="bug_report_no_title">Harap masukkan judul masalah</string>
<string name="bug_report_no_username">Please enter your valid GitHub username</string> <string name="bug_report_no_username">Mohon masukkan nama pengguna Github Anda dengan benar</string>
<string name="bug_report_summary">An unexpected error occurred. Sorry you found this bug, if it keeps crashing \"Clear app data\" or send an Email </string> <string name="bug_report_summary">Terjadi kesalahan tak terduga. Maaf jika Anda menemukan bug ini, jika terus terjadi maka \"Hapus data aplikasi\" atau kirim Email </string>
<string name="bug_report_use_account">Send using GitHub account</string> <string name="bug_report_use_account">Kirim dengan menggunakan akun Github</string>
<string name="buy_now">Buy now</string> <string name="buy_now">Beli sekarang</string>
<string name="cancel_current_timer">Cancel</string> <string name="cancel_current_timer">Batal</string>
<string name="card">Card</string> <string name="card">Kartu</string>
<string name="card_color_style">Colored Card</string> <string name="card_color_style">Kartu Berwarna</string>
<string name="card_square">Square Card</string> <string name="card_square">Kartu Persegi</string>
<string name="card_style">Card</string> <string name="card_style">Kartu</string>
<string name="carousal_effect_on_now_playing_screen">Carousel effect on the now playing screen</string> <string name="carousal_effect_on_now_playing_screen">Efek korsel pada layar yang sedang diputar</string>
<string name="cascading">Cascading</string> <string name="cascading">Bergerak</string>
<string name="changelog">Changelog</string> <string name="changelog">Daftar Log Pembaruan</string>
<string name="changelog_summary">Check out What\'s New</string> <string name="changelog_summary">Lihat Apa Yang Baru</string>
<string name="choose_restore_title">Choose what to restore</string> <string name="choose_restore_title">Pilih apa yang akan dipulihkan</string>
<string name="circle">Circle</string> <string name="circle">Bulat</string>
<string name="circular">Circular</string> <string name="circular">Melingkar</string>
<string name="classic">Classic</string> <string name="classic">Klasik</string>
<string name="clear_action">Clear</string> <string name="clear_action">Bersihkan</string>
<string name="clear_blacklist">Clear blacklist</string> <string name="clear_blacklist">Bersihkan daftar hitam</string>
<string name="clear_playing_queue">Clear queue</string> <string name="clear_playing_queue">Bersihkan antrean</string>
<string name="color">Color</string> <string name="color">Warna</string>
<string name="colors">Colors</string> <string name="colors">Warna</string>
<string name="composer">Composer</string> <string name="composer">Komposer</string>
<string name="copied_device_info_to_clipboard">Copied device info to clipboard.</string> <string name="copied_device_info_to_clipboard">Info perangkat tersalin ke papan klip.</string>
<string name="could_not_create_playlist">Couldn\u2019t create playlist.</string> <string name="could_not_create_playlist">Tidak dapat membuat daftar putar.</string>
<string name="could_not_download_album_cover">"Couldn\u2019t download a matching album cover."</string> <string name="could_not_download_album_cover">"Tidak dapat mengunduh sampul album yang cocok."</string>
<string name="could_not_restore_purchase">Could not restore purchase.</string> <string name="could_not_restore_purchase">Tidak dapat mengembalikan pembelian.</string>
<string name="could_not_scan_files">Could not scan %d files.</string> <string name="could_not_scan_files">Tidak dapat memindai %d berkas.</string>
<string name="create_action">Create</string> <string name="create_action">Buat</string>
<string name="create_new_backup">Create</string> <string name="create_new_backup">Buat</string>
<string name="created_playlist_x">Created playlist %1$s.</string> <string name="created_playlist_x">%1$s daftar putar telah dibuat.</string>
<string name="credit_title">Members and contributors </string> <string name="credit_title">Member dan kontributor </string>
<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">Saat ini mendengarkan %1$s oleh %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string> <string name="custom_artist_images">Gambar Artis Kustom</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Agak Gelap</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string> <string name="delete_playlist_title">Hapus daftar putar</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_x"><![CDATA[Hapus daftar putar <b>%1$s</b>?]]></string>
<string name="delete_playlist_x"><![CDATA[Delete the playlist <b>%1$s</b>?]]></string> <string name="delete_playlists_title">Hapus daftar putar</string>
<string name="delete_playlists_title">Delete playlists</string> <string name="delete_song_title">Hapus lagu</string>
<string name="delete_song_title">Delete song</string> <string name="delete_song_x"><![CDATA[Hapus lagu <b>%1$s</b>?]]></string>
<string name="delete_song_x"><![CDATA[Delete the song <b>%1$s</b>?]]></string> <string name="delete_songs_title">Hapus lagu</string>
<string name="delete_songs_title">Delete songs</string> <string name="delete_x_playlists"><![CDATA[Hapus <b>%1$d</b> daftar putar?]]></string>
<string name="delete_x_playlists"><![CDATA[Delete <b>%1$d</b> playlists?]]></string> <string name="delete_x_songs"><![CDATA[Hapus <b>%1$d</b> lagu?]]></string>
<string name="delete_x_songs"><![CDATA[Delete <b>%1$d</b> songs?]]></string> <string name="deleted_x_songs">Menghapus %1$d lagu.</string>
<string name="deleted_x_songs">Deleted %1$d songs.</string> <string name="deleting_songs">Menghapus Lagu</string>
<string name="deleting_songs">Deleting Songs</string> <string name="depth">Kedalaman</string>
<string name="depth">Depth</string> <string name="description">Keterangan</string>
<string name="description">Description</string> <string name="device_info">Info perangkat</string>
<string name="device_info">Device info</string> <string name="dialog_message_set_ringtone">Izinkan Retro Music untuk mengubah pengaturan audio</string>
<string name="dialog_message_set_ringtone">Allow Retro Music to modify audio settings</string> <string name="dialog_title_set_ringtone">Setel nada dering</string>
<string name="dialog_title_set_ringtone">Set ringtone</string> <string name="disc_hint">Nomer disk</string>
<string name="disc_hint">Disc Number</string> <string name="do_you_want_to_clear_the_blacklist">Apakah Anda ingin menghapus daftar hitam?</string>
<string name="do_you_want_to_clear_the_blacklist">Do you want to clear the blacklist?</string> <string name="do_you_want_to_remove_from_the_blacklist"><![CDATA[Apakah Anda ingin menghapus <b>%1$s</b> dari daftar hitam?]]></string>
<string name="do_you_want_to_remove_from_the_blacklist"><![CDATA[Do you want to remove <b>%1$s</b> from the blacklist?]]></string> <string name="donate">Donasi</string>
<string name="donate">Donate</string> <string name="donate_summary">Jika Anda pikir saya pantas dibayar untuk pekerjaan saya, Anda dapat meninggalkan sejumlah uang di sini</string>
<string name="donate_summary">If you think I deserve to get paid for my work, you can leave some money here</string> <string name="donation_header">Belikan saya:</string>
<string name="donation_header">Buy me a:</string> <string name="done">Selesai</string>
<string name="done">Done</string> <string name="drive_mode">Mode mengemudi</string>
<string name="drive_mode">Drive mode</string> <string name="edit_fab">Tombol Edit</string>
<string name="edit_fab">Edit Button</string> <string name="edit_normal_lyrics">Edit Lirik</string>
<string name="edit_normal_lyrics">Edit Lyrics</string> <string name="edit_synced_lyrics">Edit Lirik Yang Disinkronkan</string>
<string name="edit_synced_lyrics">Edit Synced Lyrics</string> <string name="empty">Kosong</string>
<string name="empty">Empty</string>
<string name="equalizer">Equalizer</string> <string name="equalizer">Equalizer</string>
<string name="faq">FAQ</string> <string name="faq">Pertanyaan Umum (FAQ)</string>
<string name="favorites">Favorites</string> <string name="favorites">Favorit</string>
<string name="finish_last_song">Finish last song</string> <string name="finish_last_song">Selesaikan lagu terakhir</string>
<string name="fit">Fit</string> <string name="fit">Paskan</string>
<string name="flat">Flat</string> <string name="flat">Datar</string>
<string name="folders">Folders</string> <string name="folders">Folder</string>
<string name="follow_system">Follow system</string> <string name="follow_system">Ikuti setelan sistem</string>
<string name="for_you">For you</string> <string name="for_you">Untuk Anda</string>
<string name="free">Free</string> <string name="free">Gratis</string>
<string name="full">Full</string> <string name="full">Penuh</string>
<string name="full_card">Full card</string> <string name="full_card">Kartu penuh</string>
<string name="general_settings_summary">Change the theme and colors of the app</string> <string name="general_settings_summary">Ubah tema dan warna aplikasi</string>
<string name="general_settings_title">Look and feel</string> <string name="general_settings_title">Tampilan dan nuansa</string>
<string name="genre">Genre</string> <string name="genre">Genre</string>
<string name="genres">Genres</string> <string name="genres">Genre</string>
<string name="git_hub_summary">Fork the project on GitHub</string> <string name="git_hub_summary">Beri fork pada proyek di GitHub</string>
<string name="gradient">Gradient</string> <string name="gradient">Gradasi</string>
<string name="grid_size_1">1</string> <string name="grid_size_1">1</string>
<string name="grid_size_2">2</string> <string name="grid_size_2">2</string>
<string name="grid_size_3">3</string> <string name="grid_size_3">3</string>
@ -188,92 +187,92 @@
<string name="grid_size_6">6</string> <string name="grid_size_6">6</string>
<string name="grid_size_7">7</string> <string name="grid_size_7">7</string>
<string name="grid_size_8">8</string> <string name="grid_size_8">8</string>
<string name="grid_style_label">Grid style</string> <string name="grid_style_label">Gaya kisi</string>
<string name="help_summary">Need more help?</string> <string name="help_summary">Butuh bantuan lebih?</string>
<string name="hinge">Hinge</string> <string name="hinge">Engsel</string>
<string name="history">History</string> <string name="history">Riwayat</string>
<string name="home">Home</string> <string name="home">Beranda</string>
<string name="horizontal_flip">Horizontal flip</string> <string name="horizontal_flip">Balik horisontal</string>
<string name="image">Image</string> <string name="image">Gambar</string>
<string name="image_gradient">Gradient image</string> <string name="image_gradient">Gambar bergradasi</string>
<string name="image_settings_summary">Change artist image download settings</string> <string name="image_settings_summary">Ubah pengaturan unduhan gambar artis</string>
<string name="import_label">Import</string> <string name="import_label">Impor</string>
<string name="import_playlist">Import playlist</string> <string name="import_playlist">Impor daftar putar</string>
<string name="import_playlist_message">It imports all playlists listed in the Android Media Store with songs, if the playlists already exists, the songs will get merged.</string> <string name="import_playlist_message">Ini mengimpor semua daftar putar yang terdaftar di Android Media Store dengan lagu, jika daftar putar sudah ada, lagu akan digabungkan.</string>
<string name="inserted_x_songs_into_playlist_x">Inserted %1$d songs into the playlist %2$s.</string> <string name="inserted_x_songs_into_playlist_x">Memasukkan %1$d lagu ke dalam daftar putar %2$s.</string>
<string name="instagram_page_summary">Share your Retro Music setup to showcase on Instagram</string> <string name="instagram_page_summary">Bagikan pengaturan Musik Retro Anda untuk dipamerkan di Instagram</string>
<string name="keyboard">Keyboard</string> <string name="keyboard">Papan ketik</string>
<string name="label_bit_rate">Bitrate</string> <string name="label_bit_rate">Bitrate</string>
<string name="label_file_format">Format</string> <string name="label_file_format">Format</string>
<string name="label_file_name">File name</string> <string name="label_file_name">Nama file</string>
<string name="label_file_path">File path</string> <string name="label_file_path">Lokasi File</string>
<string name="label_file_size">Size</string> <string name="label_file_size">Ukuran</string>
<string name="label_more_from">More from %s</string> <string name="label_more_from">Lainnya dari %s</string>
<string name="label_sampling_rate">Sampling rate</string> <string name="label_sampling_rate">Sampling rate</string>
<string name="label_track_length">Length</string> <string name="label_track_length">Durasi</string>
<string name="labeled">Labeled</string> <string name="labeled">Berlabel</string>
<string name="last_added">Last added</string> <string name="last_added">Terakhir ditambahkan</string>
<string name="last_song">Last song</string> <string name="last_song">Lagu terakhir</string>
<string name="library_categories">Library categories</string> <string name="library_categories">Kategori perpustakaan</string>
<string name="licenses">Licenses</string> <string name="licenses">Lisensi</string>
<string name="light_theme_name">Clearly White</string> <string name="light_theme_name">Putih Terang</string>
<string name="listeners_label">Listeners</string> <string name="listeners_label">Pendengar</string>
<string name="listing_files">Listing files</string> <string name="listing_files">Daftar file</string>
<string name="loading_products">Loading products</string> <string name="loading_products">Memuat produk</string>
<string name="login">Login</string> <string name="login">Masuk</string>
<string name="lyrics">Lyrics</string> <string name="lyrics">Lirik</string>
<string name="made_with_love">Made with ❤️ in India</string> <string name="made_with_love">Dibuat dengan ❤️ di India</string>
<string name="material">Material</string> <string name="material">Material</string>
<string name="md_error_label">Error</string> <string name="md_error_label">Kesalahan</string>
<string name="md_storage_perm_error">Permission error</string> <string name="md_storage_perm_error">Kesalahan izin</string>
<string name="my_name">Name</string> <string name="my_name">Nama</string>
<string name="my_top_tracks">Most played</string> <string name="my_top_tracks">Paling sering diputar</string>
<string name="never">Never</string> <string name="never">Tidak pernah</string>
<string name="new_playlist_title">Daftar putar baru</string> <string name="new_playlist_title">Daftar putar baru</string>
<string name="new_start_directory">%s is the new start directory.</string> <string name="new_start_directory">%s adalah direktori awal yang baru.</string>
<string name="next_song">Next Song</string> <string name="next_song">Lagu berikutnya</string>
<string name="no_albums">You have no albums</string> <string name="no_albums">Tidak ada album</string>
<string name="no_artists">You have no artists</string> <string name="no_artists">Tidak ada artis</string>
<string name="no_audio_ID">"Play a song first, then try again."</string> <string name="no_audio_ID">"Putar lagu terlebih dahulu, lalu coba lagi."</string>
<string name="no_backups_found">No Backups Found</string> <string name="no_backups_found">Tidak ada cadangan yang ditemukan</string>
<string name="no_equalizer">No equalizer found</string> <string name="no_equalizer">Equalizer tidak ditemukan</string>
<string name="no_genres">You have no genres</string> <string name="no_genres">Tidak ada genre</string>
<string name="no_lyrics_found">No lyrics found</string> <string name="no_lyrics_found">Lirik tidak ditemukan</string>
<string name="no_playing_queue">No songs playing</string> <string name="no_playing_queue">Tidak ada lagu yang diputar</string>
<string name="no_playlists">You have no playlists</string> <string name="no_playlists">Tidak ada daftar putar</string>
<string name="no_purchase_found">No purchase found.</string> <string name="no_purchase_found">Tidak ada pembelian yang ditemukan.</string>
<string name="no_results">No results</string> <string name="no_results">Tidak ada hasil</string>
<string name="no_songs">You have no songs</string> <string name="no_songs">Tidak ada lagu</string>
<string name="normal">Normal</string> <string name="normal">Normal</string>
<string name="normal_lyrics">Normal lyrics</string> <string name="normal_lyrics">Lirik normal</string>
<string name="not_listed_in_media_store"><![CDATA[<b>%s</b> is not listed in the media store.]]></string> <string name="not_listed_in_media_store"><![CDATA[<b>%s</b> tidak terdaftar di penyimpanan media.]]></string>
<string name="not_recently_played">Not recently played</string> <string name="not_recently_played">Tidak baru-baru ini dimainkan</string>
<string name="nothing_to_scan">Nothing to scan.</string> <string name="nothing_to_scan">Tidak ada yang di pindai.</string>
<string name="nothing_to_see">Nothing to see</string> <string name="nothing_to_see">Tidak ada yang bisa dilihat</string>
<string name="notification">Notification</string> <string name="notification">Pemberitahuan</string>
<string name="notification_settings_summary">Customize the notification style</string> <string name="notification_settings_summary">Sesuaikan gaya notifikasi</string>
<string name="now_playing">Now playing</string> <string name="now_playing">Sedang diputar</string>
<string name="now_playing_queue">Now playing queue</string> <string name="now_playing_queue">Sekarang memutar antrean</string>
<string name="now_playing_summary">Customize the now playing screen</string> <string name="now_playing_summary">Sesuaikan tampilan layar yang sedang diputar</string>
<string name="now_playing_themes">9+ now playing themes</string> <string name="now_playing_themes">9+ tema pemutar sekarang</string>
<string name="only_on_wifi">Only on Wi-Fi</string> <string name="only_on_wifi">Hanya di Wifi</string>
<string name="other_settings_summary">Advanced testing features</string> <string name="other_settings_summary">Fitur pengujian lanjutan</string>
<string name="others">Other</string> <string name="others">Lainnya</string>
<string name="password">Password</string> <string name="password">Kata sandi</string>
<string name="past_three_months">Past 3 months</string> <string name="past_three_months">3 bulan terakhir</string>
<string name="paste_lyrics_here">Paste Lyrics Here</string> <string name="paste_lyrics_here">Tempel Lirik Disini</string>
<string name="paste_timeframe_lyrics_here">Paste timeframe lyrics here</string> <string name="paste_timeframe_lyrics_here">Tempel kerangka waktu lirik di sini</string>
<string name="peak">Peak</string> <string name="peak">Puncak</string>
<string name="permission_external_storage_denied">Permission to access external storage denied.</string> <string name="permission_external_storage_denied">Izin untuk mengakses penyimpanan eksternal ditolak.</string>
<string name="permission_summary">The app needs permission to access your device storage for playing music</string> <string name="permission_summary">Aplikasi memerlukan izin untuk mengakses penyimpanan perangkat Anda untuk memutar musik</string>
<string name="permission_title">Storage Access</string> <string name="permission_title">Akses Penyimpanan</string>
<string name="permissions_denied">Permissions denied.</string> <string name="permissions_denied">Izin ditolak.</string>
<string name="personalize">Personalize</string> <string name="personalize">Personalisasi</string>
<string name="personalize_settings_summary">Customize your now playing and UI controls</string> <string name="personalize_settings_summary">Sesuaikan pemutar dan UI Anda</string>
<string name="pick_from_local_storage">Pick from local storage</string> <string name="pick_from_local_storage">Pilih dari penyimpanan lokal</string>
<string name="pinterest_page">Pinterest</string> <string name="pinterest_page">Pinterest</string>
<string name="pinterest_page_summary">Follow Pinterest page for Retro Music design inspiration</string> <string name="pinterest_page_summary">Ikuti halaman Pinterest untuk inspirasi desain Retro Music</string>
<string name="plain">Plain</string> <string name="plain">Polos</string>
<string name="playing_notification_description">The playing notification provides actions for play/pause etc.</string> <string name="playing_notification_description">The playing notification provides actions for play/pause etc.</string>
<string name="playing_notification_name">Playing notification</string> <string name="playing_notification_name">Playing notification</string>
<string name="playlist_is_empty">Playlist is empty</string> <string name="playlist_is_empty">Playlist is empty</string>
@ -286,7 +285,7 @@
<string name="pref_header_advanced">Advanced</string> <string name="pref_header_advanced">Advanced</string>
<string name="pref_header_album">Album style</string> <string name="pref_header_album">Album style</string>
<string name="pref_header_audio">Audio</string> <string name="pref_header_audio">Audio</string>
<string name="pref_header_blacklist">Blacklist</string> <string name="pref_header_blacklist">Daftar hitam</string>
<string name="pref_header_controls">Controls</string> <string name="pref_header_controls">Controls</string>
<string name="pref_header_general">Theme</string> <string name="pref_header_general">Theme</string>
<string name="pref_header_images">Images</string> <string name="pref_header_images">Images</string>
@ -337,7 +336,7 @@
<string name="pref_title_audio_ducking">Reduce volume on focus loss</string> <string name="pref_title_audio_ducking">Reduce volume on focus loss</string>
<string name="pref_title_audio_fade">Fade Audio</string> <string name="pref_title_audio_fade">Fade Audio</string>
<string name="pref_title_auto_download_artist_images">Auto-download artist images</string> <string name="pref_title_auto_download_artist_images">Auto-download artist images</string>
<string name="pref_title_blacklist">Blacklist</string> <string name="pref_title_blacklist">Daftar hitam</string>
<string name="pref_title_bluetooth_playback">Bluetooth playback</string> <string name="pref_title_bluetooth_playback">Bluetooth playback</string>
<string name="pref_title_blurred_album_art">Blur album cover</string> <string name="pref_title_blurred_album_art">Blur album cover</string>
<string name="pref_title_classic_notification">Classic notification design</string> <string name="pref_title_classic_notification">Classic notification design</string>
@ -428,7 +427,7 @@
<string name="share_summary">Share the app with your friends and family</string> <string name="share_summary">Share the app with your friends and family</string>
<string name="share_to_stories">Share to Stories</string> <string name="share_to_stories">Share to Stories</string>
<string name="show_album_artists">Show Album Artists</string> <string name="show_album_artists">Show Album Artists</string>
<string name="shuffle">Shuffle</string> <string name="shuffle">Acak</string>
<string name="simple">Simple</string> <string name="simple">Simple</string>
<string name="sleep_timer_canceled">Sleep timer canceled.</string> <string name="sleep_timer_canceled">Sleep timer canceled.</string>
<string name="sleep_timer_set">Sleep timer set for %d minutes from now.</string> <string name="sleep_timer_set">Sleep timer set for %d minutes from now.</string>
@ -441,8 +440,8 @@
<string name="sort_order_a_z">Ascending</string> <string name="sort_order_a_z">Ascending</string>
<string name="sort_order_album">Album</string> <string name="sort_order_album">Album</string>
<string name="sort_order_album_artist">@string/album_artist</string> <string name="sort_order_album_artist">@string/album_artist</string>
<string name="sort_order_artist">Artist</string> <string name="sort_order_artist">Artis</string>
<string name="sort_order_composer">Composer</string> <string name="sort_order_composer">Komposer</string>
<string name="sort_order_date">Date added</string> <string name="sort_order_date">Date added</string>
<string name="sort_order_date_modified">Date modified</string> <string name="sort_order_date_modified">Date modified</string>
<string name="sort_order_num_songs">Song count</string> <string name="sort_order_num_songs">Song count</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>

Some files were not shown because too many files have changed in this diff Show more