Merge pull request #1176 from prathameshmm02/dev

Bug Fixes & Improvements
main
Daksh P. Jain 2021-12-01 11:56:31 +05:30 committed by GitHub
commit 6924e5de01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
137 changed files with 1043 additions and 1837 deletions

View File

@ -9,7 +9,7 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 31
renderscriptTargetApi 29//must match target sdk and build tools renderscriptTargetApi 29//must match target sdk and build tools
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -126,7 +126,7 @@ dependencies {
def retrofit_version = '2.9.0' def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version" implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2' implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3'
def material_dialog_version = "3.3.0" def material_dialog_version = "3.3.0"
implementation "com.afollestad.material-dialogs:core:$material_dialog_version" implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
@ -142,7 +142,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
def koin_version = '3.1.3' def koin_version = '3.1.4'
implementation "io.insert-koin:koin-core:$koin_version" implementation "io.insert-koin:koin-core:$koin_version"
implementation "io.insert-koin:koin-android:$koin_version" implementation "io.insert-koin:koin-android:$koin_version"
@ -157,7 +157,7 @@ dependencies {
implementation 'com.anjlab.android.iab.v3:library:2.0.3' implementation 'com.anjlab.android.iab.v3:library:2.0.3'
implementation 'com.r0adkll:slidableactivity:2.1.0' implementation 'com.r0adkll:slidableactivity:2.1.0'
implementation 'com.heinrichreimersoftware:material-intro:2.0.0' implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
implementation 'com.github.dhaval2404:imagepicker:1.7.1' implementation 'com.github.dhaval2404:imagepicker:2.1'
implementation 'me.zhanghai.android.fastscroll:library:1.1.7' implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
implementation 'cat.ereza:customactivityoncrash:2.3.0' implementation 'cat.ereza:customactivityoncrash:2.3.0'
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6' debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'

View File

@ -262,6 +262,7 @@
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:foregroundServiceType="mediaPlayback"
tools:ignore="ExportedService"> tools:ignore="ExportedService">
<intent-filter> <intent-filter>
<action android:name="android.media.browse.MediaBrowserService" /> <action android:name="android.media.browse.MediaBrowserService" />

View File

@ -37,6 +37,7 @@ object Constants {
const val IS_MUSIC = const val IS_MUSIC =
MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''" MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"
@Suppress("Deprecation")
val baseProjection = arrayOf( val baseProjection = arrayOf(
BaseColumns._ID, // 0 BaseColumns._ID, // 0
MediaStore.Audio.AudioColumns.TITLE, // 1 MediaStore.Audio.AudioColumns.TITLE, // 1
@ -118,6 +119,8 @@ const val ALBUM_GRID_SIZE_LAND = "album_grid_size_land"
const val SONG_GRID_SIZE_LAND = "song_grid_size_land" const val SONG_GRID_SIZE_LAND = "song_grid_size_land"
const val ARTIST_GRID_SIZE = "artist_grid_size" const val ARTIST_GRID_SIZE = "artist_grid_size"
const val ARTIST_GRID_SIZE_LAND = "artist_grid_size_land" const val ARTIST_GRID_SIZE_LAND = "artist_grid_size_land"
const val PLAYLIST_GRID_SIZE = "playlist_grid_size"
const val PLAYLIST_GRID_SIZE_LAND = "playlist_grid_size_land"
const val COLORED_APP_SHORTCUTS = "colored_app_shortcuts" const val COLORED_APP_SHORTCUTS = "colored_app_shortcuts"
const val AUDIO_DUCKING = "audio_ducking" const val AUDIO_DUCKING = "audio_ducking"
const val LAST_ADDED_CUTOFF = "last_added_interval" const val LAST_ADDED_CUTOFF = "last_added_interval"

View File

@ -29,18 +29,10 @@ public class LanguageContextWrapper extends ContextWrapper {
LocaleList localeList = new LocaleList(newLocale); LocaleList localeList = new LocaleList(newLocale);
LocaleList.setDefault(localeList); LocaleList.setDefault(localeList);
configuration.setLocales(localeList); configuration.setLocales(localeList);
context = context.createConfigurationContext(configuration);
} else if (VersionUtils.INSTANCE.hasLollipop()) {
configuration.setLocale(newLocale);
context = context.createConfigurationContext(configuration);
} else { } else {
configuration.locale = newLocale; configuration.setLocale(newLocale);
res.updateConfiguration(configuration, res.getDisplayMetrics());
} }
context = context.createConfigurationContext(configuration);
return new LanguageContextWrapper(context); return new LanguageContextWrapper(context);
} }
} }

View File

@ -1,46 +0,0 @@
package code.name.monkey.retromusic
import android.content.Context
import android.util.AttributeSet
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class PeekingLinearLayoutManager : LinearLayoutManager {
@JvmOverloads
constructor(
context: Context?,
@RecyclerView.Orientation orientation: Int = RecyclerView.VERTICAL,
reverseLayout: Boolean = false
) : super(context, orientation, reverseLayout)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(
context,
attrs,
defStyleAttr,
defStyleRes
)
override fun generateDefaultLayoutParams() =
scaledLayoutParams(super.generateDefaultLayoutParams())
override fun generateLayoutParams(lp: ViewGroup.LayoutParams?) =
scaledLayoutParams(super.generateLayoutParams(lp))
override fun generateLayoutParams(c: Context?, attrs: AttributeSet?) =
scaledLayoutParams(super.generateLayoutParams(c, attrs))
private fun scaledLayoutParams(layoutParams: RecyclerView.LayoutParams) =
layoutParams.apply {
when (orientation) {
HORIZONTAL -> width = (horizontalSpace * ratio).toInt()
VERTICAL -> height = (verticalSpace * ratio).toInt()
}
}
private val horizontalSpace get() = width - paddingStart - paddingEnd
private val verticalSpace get() = height - paddingTop - paddingBottom
private val ratio = 0.8f // change to 0.7f for 70%
}

View File

@ -82,6 +82,7 @@ class LockScreenActivity : AbsMusicServiceActivity() {
} }
} }
@Suppress("Deprecation")
private fun lockScreenInit() { private fun lockScreenInit() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true) setShowWhenLocked(true)

View File

@ -132,7 +132,7 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
permissionDeniedMessage!!, permissionDeniedMessage!!,
Snackbar.LENGTH_INDEFINITE Snackbar.LENGTH_INDEFINITE
) )
.setAction(code.name.monkey.retromusic.R.string.action_grant) { requestPermissions() } .setAction(R.string.action_grant) { requestPermissions() }
.setActionTextColor(ThemeStore.accentColor(this)).show() .setActionTextColor(ThemeStore.accentColor(this)).show()
} else { } else {
// User has deny permission and checked never show permission dialog so you can redirect to Application settings page // User has deny permission and checked never show permission dialog so you can redirect to Application settings page
@ -140,7 +140,7 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
snackBarContainer, snackBarContainer,
permissionDeniedMessage!!, permissionDeniedMessage!!,
Snackbar.LENGTH_INDEFINITE Snackbar.LENGTH_INDEFINITE
).setAction(code.name.monkey.retromusic.R.string.action_settings) { ).setAction(R.string.action_settings) {
val intent = Intent() val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts( val uri = Uri.fromParts(
@ -172,7 +172,7 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
v.getGlobalVisibleRect(outRect) v.getGlobalVisibleRect(outRect)
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
v.clearFocus() v.clearFocus()
val imm = getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(v.windowToken, 0) getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(v.windowToken, 0)
} }
} }
} }

View File

@ -25,6 +25,8 @@ import android.view.WindowManager
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.core.os.ConfigurationCompat import androidx.core.os.ConfigurationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import code.name.monkey.appthemehelper.ATH import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity import code.name.monkey.appthemehelper.common.ATHToolbarActivity
@ -118,12 +120,11 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
if (statusBar != null) { if (statusBar != null) {
when { when {
VersionUtils.hasMarshmallow() -> statusBar.setBackgroundColor(color) VersionUtils.hasMarshmallow() -> statusBar.setBackgroundColor(color)
VersionUtils.hasLollipop() -> statusBar.setBackgroundColor( else -> statusBar.setBackgroundColor(
ColorUtil.darkenColor( ColorUtil.darkenColor(
color color
) )
) )
else -> statusBar.setBackgroundColor(color)
} }
} else { } else {
when { when {
@ -193,16 +194,20 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
} }
private fun setImmersiveFullscreen() { private fun setImmersiveFullscreen() {
val flags =
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
if (PreferenceUtil.isFullScreenMode) { if (PreferenceUtil.isFullScreenMode) {
window.decorView.systemUiVisibility = flags WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
hide(WindowInsetsCompat.Type.systemBars())
}
} }
} }
private fun exitFullscreen() { private fun exitFullscreen() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
show(WindowInsetsCompat.Type.systemBars())
}
} }
override fun run() { override fun run() {

View File

@ -7,6 +7,7 @@ import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import androidx.annotation.IntRange; import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
@ -16,19 +17,16 @@ import code.name.monkey.retromusic.util.PreferenceUtil;
public class DeviceInfo { public class DeviceInfo {
@SuppressLint("NewApi") @SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private final String[] abis = private final String[] abis =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP Build.SUPPORTED_ABIS;
? Build.SUPPORTED_ABIS
: new String[] {Build.CPU_ABI, Build.CPU_ABI2};
@SuppressLint("NewApi") @SuppressLint("NewApi")
private final String[] abis32Bits = private final String[] abis32Bits =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.SUPPORTED_32_BIT_ABIS : null; Build.SUPPORTED_32_BIT_ABIS;
@SuppressLint("NewApi") @SuppressLint("NewApi")
private final String[] abis64Bits = private final String[] abis64Bits =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.SUPPORTED_64_BIT_ABIS : null; Build.SUPPORTED_64_BIT_ABIS;
private final String baseTheme; private final String baseTheme;
@ -138,6 +136,7 @@ public class DeviceInfo {
+ "</table>\n"; + "</table>\n";
} }
@NonNull
@Override @Override
public String toString() { public String toString() {
return "App version: " return "App version: "

View File

@ -49,9 +49,9 @@ class TagWriter {
) )
} }
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) = suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { kotlin.runCatching {
var artwork: Artwork? = null var artwork: Artwork? = null
var albumArtFile: File? = null var albumArtFile: File? = null
if (info.artworkInfo?.artwork != null) { if (info.artworkInfo?.artwork != null) {
@ -113,18 +113,18 @@ class TagWriter {
deleteAlbumArt(context, info.artworkInfo!!.albumId) deleteAlbumArt(context, info.artworkInfo!!.albumId)
} }
scan(context, info.filePaths) scan(context, info.filePaths)
} catch (e: Exception) { }.onFailure {
e.printStackTrace() it.printStackTrace()
scan(context, null)
} }
} }
}
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo) = suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val cacheFiles = mutableListOf<File>() val cacheFiles = mutableListOf<File>()
try { kotlin.runCatching {
var artwork: Artwork? = null var artwork: Artwork? = null
var albumArtFile: File? = null var albumArtFile: File? = null
if (info.artworkInfo?.artwork != null) { if (info.artworkInfo?.artwork != null) {
@ -193,11 +193,10 @@ class TagWriter {
} else if (deletedArtwork) { } else if (deletedArtwork) {
deleteAlbumArt(context, info.artworkInfo!!.albumId) deleteAlbumArt(context, info.artworkInfo!!.albumId)
} }
cacheFiles }.onFailure {
} catch (e: Exception) { it.printStackTrace()
e.printStackTrace()
listOf()
} }
cacheFiles
} }
} }
} }

View File

@ -14,6 +14,7 @@
*/ */
package code.name.monkey.retromusic.adapter package code.name.monkey.retromusic.adapter
import android.os.SystemClock
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -51,6 +52,8 @@ class HomeAdapter(
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IArtistClickListener, IAlbumClickListener, ) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IArtistClickListener, IAlbumClickListener,
IGenreClickListener { IGenreClickListener {
private var mLastClickTime: Long = 0
private var list = listOf<Home>() private var list = listOf<Home>()
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
@ -194,6 +197,10 @@ class HomeAdapter(
itemView.findViewById<TextView>(R.id.message).apply { itemView.findViewById<TextView>(R.id.message).apply {
setTextColor(color) setTextColor(color)
setOnClickListener { setOnClickListener {
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000){
return@setOnClickListener
}
mLastClickTime = SystemClock.elapsedRealtime()
MusicPlayerRemote.playNext((home.arrayList as List<Song>).subList(0, 8)) MusicPlayerRemote.playNext((home.arrayList as List<Song>).subList(0, 8))
if (!MusicPlayerRemote.isPlaying) { if (!MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.playNextSong() MusicPlayerRemote.playNextSong()

View File

@ -137,16 +137,6 @@ class SearchAdapter(
} }
} }
private fun getSongs(playlist: Playlist): List<Song> {
val songs = mutableListOf<Song>()
if (playlist is AbsSmartPlaylist) {
songs.addAll(playlist.getSongs())
} else {
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
}
return songs
}
override fun getItemCount(): Int { override fun getItemCount(): Int {
return dataSet.size return dataSet.size
} }

View File

@ -1,66 +0,0 @@
/*
* Copyright (c) 2020 Hemanth Savarla.
*
* 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.app.Activity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.model.Contributor
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.views.RetroShapeableImageView
class TranslatorsAdapter(
private var contributors: List<Contributor>
) : RecyclerView.Adapter<TranslatorsAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_contributor,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val contributor = contributors[position]
holder.bindData(contributor)
holder.itemView.setOnClickListener {
RetroUtil.openUrl(it?.context as Activity, contributors[position].link)
}
}
override fun getItemCount(): Int {
return contributors.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val title: TextView = itemView.findViewById(R.id.title)
val text: TextView = itemView.findViewById(R.id.text)
val image: RetroShapeableImageView = itemView.findViewById(R.id.icon)
internal fun bindData(contributor: Contributor) {
title.text = contributor.name
text.text = contributor.summary
image.hide()
}
}
}

View File

@ -27,13 +27,13 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.fragments.AlbumCoverStyle import code.name.monkey.retromusic.fragments.AlbumCoverStyle
import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.fragments.base.goToLyrics
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
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.NavigationUtil
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 com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
@ -127,7 +127,8 @@ class AlbumCoverPagerAdapter(
setTitle(song.title) setTitle(song.title)
setMessage(if (data.isNullOrEmpty()) "No lyrics found" else data) setMessage(if (data.isNullOrEmpty()) "No lyrics found" else data)
setNegativeButton(R.string.synced_lyrics) { _, _ -> setNegativeButton(R.string.synced_lyrics) { _, _ ->
NavigationUtil.goToLyrics(requireActivity())
goToLyrics(requireActivity())
} }
show() show()
} }

View File

@ -22,6 +22,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.setPadding
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
@ -29,6 +30,7 @@ import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.toSongs import code.name.monkey.retromusic.db.toSongs
import code.name.monkey.retromusic.extensions.dipToPix
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
@ -42,7 +44,7 @@ import code.name.monkey.retromusic.util.MusicUtil
class PlaylistAdapter( class PlaylistAdapter(
override val activity: FragmentActivity, override val activity: FragmentActivity,
private var dataSet: List<PlaylistWithSongs>, var dataSet: List<PlaylistWithSongs>,
private var itemLayoutRes: Int, private var itemLayoutRes: Int,
ICabHolder: ICabHolder?, ICabHolder: ICabHolder?,
private val listener: IPlaylistClickListener private val listener: IPlaylistClickListener
@ -94,7 +96,12 @@ class PlaylistAdapter(
holder.menu?.show() holder.menu?.show()
} }
GlideApp.with(activity) GlideApp.with(activity)
.load(PlaylistPreview(playlist)) .load(
if (itemLayoutRes == R.layout.item_list) {
holder.image?.setPadding(activity.dipToPix(8F).toInt())
R.drawable.ic_playlist_play
} else PlaylistPreview(playlist)
)
.playlistOptions() .playlistOptions()
.into(holder.image!!) .into(holder.image!!)
} }

View File

@ -188,7 +188,7 @@ class PlayingQueueAdapter(
private const val UP_NEXT = 2 private const val UP_NEXT = 2
} }
override fun onSwipeItem(holder: ViewHolder, position: Int, result: Int): SwipeResultAction? { override fun onSwipeItem(holder: ViewHolder, position: Int, result: Int): SwipeResultAction {
return if (result == SwipeableItemConstants.RESULT_CANCELED) { return if (result == SwipeableItemConstants.RESULT_CANCELED) {
SwipeResultActionDefault() SwipeResultActionDefault()
} else { } else {

View File

@ -1,62 +0,0 @@
/*
* Copyright (c) 2020 Hemanth Savarla.
*
* 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.song
import android.view.MenuItem
import android.view.View
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song
class PlaylistSongAdapter(
private val playlist: PlaylistEntity,
activity: FragmentActivity,
dataSet: MutableList<Song>,
itemLayoutRes: Int,
iCabHolder: ICabHolder?
) : SongAdapter(activity, dataSet, itemLayoutRes, iCabHolder) {
init {
this.setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection)
}
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
return ViewHolder(view)
}
open inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView) {
override var songMenuRes: Int
get() = R.menu.menu_item_playlist_song
set(value) {
super.songMenuRes = value
}
override fun onSongMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_remove_from_playlist -> {
RemoveSongFromPlaylistDialog.create(song.toSongEntity(playlist.playListId))
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
return true
}
}
return super.onSongMenuItemClick(item)
}
}
}

View File

@ -42,6 +42,6 @@ class LastAddedShortcutType(context: Context) : BaseShortcutType(context) {
companion object { companion object {
val id: String val id: String
get() = BaseShortcutType.ID_PREFIX + "last_added" get() = ID_PREFIX + "last_added"
} }
} }

View File

@ -40,6 +40,6 @@ class TopTracksShortcutType(context: Context) : BaseShortcutType(context) {
companion object { companion object {
val id: String val id: String
get() = BaseShortcutType.ID_PREFIX + "top_tracks" get() = ID_PREFIX + "top_tracks"
} }
} }

View File

@ -24,6 +24,7 @@ import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
@ -61,7 +62,7 @@ class AppWidgetBig : BaseAppWidget() {
context, context,
R.drawable.ic_skip_next, R.drawable.ic_skip_next,
MaterialValueHelper.getPrimaryTextColor(context, false) MaterialValueHelper.getPrimaryTextColor(context, false)
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -70,16 +71,16 @@ class AppWidgetBig : BaseAppWidget() {
context, context,
R.drawable.ic_skip_previous, R.drawable.ic_skip_previous,
MaterialValueHelper.getPrimaryTextColor(context, false) MaterialValueHelper.getPrimaryTextColor(context, false)
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, BaseAppWidget.createBitmap( R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
context, context,
R.drawable.ic_play_arrow_white_32dp, R.drawable.ic_play_arrow_white_32dp,
MaterialValueHelper.getPrimaryTextColor(context, false) MaterialValueHelper.getPrimaryTextColor(context, false)
)!!, 1f ), 1f
) )
) )
@ -126,7 +127,7 @@ class AppWidgetBig : BaseAppWidget() {
service, service,
playPauseRes, playPauseRes,
primaryColor primaryColor
)!!, 1f ), 1f
) )
) )
@ -137,7 +138,7 @@ class AppWidgetBig : BaseAppWidget() {
service, service,
R.drawable.ic_skip_next, R.drawable.ic_skip_next,
primaryColor primaryColor
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -146,7 +147,7 @@ class AppWidgetBig : BaseAppWidget() {
service, service,
R.drawable.ic_skip_previous, R.drawable.ic_skip_previous,
primaryColor primaryColor
)!!, 1f ), 1f
) )
) )
@ -155,7 +156,7 @@ class AppWidgetBig : BaseAppWidget() {
// Load the album cover async and push the update on completion // Load the album cover async and push the update on completion
val p = RetroUtil.getScreenSize(service) val p = RetroUtil.getScreenSize(service)
val widgetImageSize = Math.min(p.x, p.y) val widgetImageSize = p.x.coerceAtMost(p.y)
val appContext = service.applicationContext val appContext = service.applicationContext
service.runOnUiThread { service.runOnUiThread {
if (target != null) { if (target != null) {
@ -208,7 +209,11 @@ class AppWidgetBig : BaseAppWidget() {
// Home // Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
var pendingIntent = var pendingIntent =
PendingIntent.getActivity(context, 0, action, PendingIntent.FLAG_IMMUTABLE) PendingIntent.getActivity(
context, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
views.setOnClickPendingIntent(R.id.clickable_area, pendingIntent) views.setOnClickPendingIntent(R.id.clickable_area, pendingIntent)
// Previous track // Previous track

View File

@ -24,6 +24,7 @@ import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
@ -59,7 +60,7 @@ class AppWidgetCard : BaseAppWidget() {
context, context,
R.drawable.ic_skip_next, R.drawable.ic_skip_next,
secondaryColor secondaryColor
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -68,7 +69,7 @@ class AppWidgetCard : BaseAppWidget() {
context, context,
R.drawable.ic_skip_previous, R.drawable.ic_skip_previous,
secondaryColor secondaryColor
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -77,7 +78,7 @@ class AppWidgetCard : BaseAppWidget() {
context, context,
R.drawable.ic_play_arrow_white_32dp, R.drawable.ic_play_arrow_white_32dp,
secondaryColor secondaryColor
)!!, 1f ), 1f
) )
) )
@ -112,7 +113,7 @@ class AppWidgetCard : BaseAppWidget() {
service, service,
playPauseRes, playPauseRes,
MaterialValueHelper.getSecondaryTextColor(service, true) MaterialValueHelper.getSecondaryTextColor(service, true)
)!!, 1f ), 1f
) )
) )
@ -123,7 +124,7 @@ class AppWidgetCard : BaseAppWidget() {
service, service,
R.drawable.ic_skip_next, R.drawable.ic_skip_next,
MaterialValueHelper.getSecondaryTextColor(service, true) MaterialValueHelper.getSecondaryTextColor(service, true)
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -132,7 +133,7 @@ class AppWidgetCard : BaseAppWidget() {
service, service,
R.drawable.ic_skip_previous, R.drawable.ic_skip_previous,
MaterialValueHelper.getSecondaryTextColor(service, true) MaterialValueHelper.getSecondaryTextColor(service, true)
)!!, 1f ), 1f
) )
) )
@ -231,7 +232,11 @@ class AppWidgetCard : BaseAppWidget() {
// Home // Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
var pendingIntent = var pendingIntent =
PendingIntent.getActivity(context, 0, action, PendingIntent.FLAG_IMMUTABLE) PendingIntent.getActivity(
context, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
views.setOnClickPendingIntent(R.id.image, pendingIntent) views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent) views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)

View File

@ -25,6 +25,7 @@ import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
@ -60,7 +61,7 @@ class AppWidgetClassic : BaseAppWidget() {
context, context,
R.drawable.ic_skip_next, R.drawable.ic_skip_next,
MaterialValueHelper.getSecondaryTextColor(context, true) MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -70,7 +71,7 @@ class AppWidgetClassic : BaseAppWidget() {
context, context,
R.drawable.ic_skip_previous, R.drawable.ic_skip_previous,
MaterialValueHelper.getSecondaryTextColor(context, true) MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -80,7 +81,7 @@ class AppWidgetClassic : BaseAppWidget() {
context, context,
R.drawable.ic_play_arrow_white_32dp, R.drawable.ic_play_arrow_white_32dp,
MaterialValueHelper.getSecondaryTextColor(context, true) MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f ), 1f
) )
) )
@ -221,7 +222,11 @@ class AppWidgetClassic : BaseAppWidget() {
// Home // Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
var pendingIntent = PendingIntent.getActivity(context, 0, action, PendingIntent.FLAG_IMMUTABLE) var pendingIntent = PendingIntent.getActivity(
context, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
views.setOnClickPendingIntent(R.id.image, pendingIntent) views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent) views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)

View File

@ -24,6 +24,7 @@ import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
@ -58,7 +59,7 @@ class AppWidgetSmall : BaseAppWidget() {
context, context,
R.drawable.ic_skip_next, R.drawable.ic_skip_next,
MaterialValueHelper.getSecondaryTextColor(context, true) MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -68,7 +69,7 @@ class AppWidgetSmall : BaseAppWidget() {
context, context,
R.drawable.ic_skip_previous, R.drawable.ic_skip_previous,
MaterialValueHelper.getSecondaryTextColor(context, true) MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -78,7 +79,7 @@ class AppWidgetSmall : BaseAppWidget() {
context, context,
R.drawable.ic_play_arrow_white_32dp, R.drawable.ic_play_arrow_white_32dp,
MaterialValueHelper.getSecondaryTextColor(context, true) MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f ), 1f
) )
) )
@ -160,7 +161,7 @@ class AppWidgetSmall : BaseAppWidget() {
R.id.button_toggle_play_pause, createBitmap( R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, playPauseRes, color service, playPauseRes, color
)!!, 1f ), 1f
) )
) )
@ -169,14 +170,14 @@ class AppWidgetSmall : BaseAppWidget() {
R.id.button_next, createBitmap( R.id.button_next, createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, R.drawable.ic_skip_next, color service, R.drawable.ic_skip_next, color
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
R.id.button_prev, createBitmap( R.id.button_prev, createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, R.drawable.ic_skip_previous, color service, R.drawable.ic_skip_previous, color
)!!, 1f ), 1f
) )
) )
@ -207,7 +208,11 @@ class AppWidgetSmall : BaseAppWidget() {
// Home // Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
var pendingIntent = var pendingIntent =
PendingIntent.getActivity(context, 0, action, PendingIntent.FLAG_IMMUTABLE) PendingIntent.getActivity(
context, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
views.setOnClickPendingIntent(R.id.image, pendingIntent) views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent) views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)

View File

@ -18,11 +18,11 @@ import android.app.PendingIntent
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.provider.MediaStore
import android.text.TextUtils import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
@ -42,7 +42,7 @@ class AppWidgetText : BaseAppWidget() {
context, R.drawable.ic_skip_next, ContextCompat.getColor( context, R.drawable.ic_skip_next, ContextCompat.getColor(
context, R.color.md_white_1000 context, R.color.md_white_1000
) )
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -51,7 +51,7 @@ class AppWidgetText : BaseAppWidget() {
context, R.drawable.ic_skip_previous, ContextCompat.getColor( context, R.drawable.ic_skip_previous, ContextCompat.getColor(
context, R.color.md_white_1000 context, R.color.md_white_1000
) )
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -60,7 +60,7 @@ class AppWidgetText : BaseAppWidget() {
context, R.drawable.ic_play_arrow_white_32dp, ContextCompat.getColor( context, R.drawable.ic_play_arrow_white_32dp, ContextCompat.getColor(
context, R.color.md_white_1000 context, R.color.md_white_1000
) )
)!!, 1f ), 1f
) )
) )
@ -89,7 +89,11 @@ class AppWidgetText : BaseAppWidget() {
// Home // Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
var pendingIntent = PendingIntent.getActivity(context, 0, action, PendingIntent.FLAG_IMMUTABLE) var pendingIntent = PendingIntent.getActivity(
context, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
views.setOnClickPendingIntent(R.id.image, pendingIntent) views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent) views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
@ -132,7 +136,7 @@ class AppWidgetText : BaseAppWidget() {
App.getContext(), playPauseRes, ContextCompat.getColor( App.getContext(), playPauseRes, ContextCompat.getColor(
App.getContext(), R.color.md_white_1000 App.getContext(), R.color.md_white_1000
) )
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -143,7 +147,7 @@ class AppWidgetText : BaseAppWidget() {
ContextCompat.getColor( ContextCompat.getColor(
App.getContext(), R.color.md_white_1000 App.getContext(), R.color.md_white_1000
) )
)!!, 1f ), 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
@ -154,7 +158,7 @@ class AppWidgetText : BaseAppWidget() {
ContextCompat.getColor( ContextCompat.getColor(
App.getContext(), R.color.md_white_1000 App.getContext(), R.color.md_white_1000
) )
)!!, 1f ), 1f
) )
) )

View File

@ -28,6 +28,7 @@ import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import android.widget.RemoteViews import android.widget.RemoteViews
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
@ -99,7 +100,11 @@ abstract class BaseAppWidget : AppWidgetProvider() {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
} else { } else {
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) PendingIntent.getService(
context, 0, intent, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
} }
} }

View File

@ -11,6 +11,7 @@ import com.google.android.gms.cast.MediaInfo.STREAM_TYPE_BUFFERED
import com.google.android.gms.cast.MediaMetadata.* import com.google.android.gms.cast.MediaMetadata.*
import com.google.android.gms.cast.framework.CastSession import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.common.images.WebImage import com.google.android.gms.common.images.WebImage
import org.json.JSONObject
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
@ -41,7 +42,7 @@ object CastHelper {
position, position,
MediaStatus.REPEAT_MODE_REPEAT_OFF, MediaStatus.REPEAT_MODE_REPEAT_OFF,
progress, progress,
null JSONObject()
) )
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()

View File

@ -13,7 +13,7 @@ class ExpandedControlsActivity : ExpandedControllerActivity() {
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.menu_cast, menu) menuInflater.inflate(R.menu.menu_cast, menu)
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.action_cast) CastButtonFactory.setUpMediaRouteButton(this, menu!!, R.id.action_cast)
return true return true
} }
} }

View File

@ -17,7 +17,7 @@ class RetroWebServer(val context: Context) : NanoHTTPD(SERVER_PORT) {
const val PART_COVER_ART = "coverart" const val PART_COVER_ART = "coverart"
const val PART_SONG = "song" const val PART_SONG = "song"
const val PARAM_ID = "id" const val PARAM_ID = "id"
var mRetroWebServer: RetroWebServer? = null private var mRetroWebServer: RetroWebServer? = null
fun getInstance(context: Context): RetroWebServer { fun getInstance(context: Context): RetroWebServer {
if (mRetroWebServer == null) { if (mRetroWebServer == null) {
mRetroWebServer = RetroWebServer(context) mRetroWebServer = RetroWebServer(context)

View File

@ -26,6 +26,8 @@ interface HistoryDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertSongInHistory(historyEntity: HistoryEntity) suspend fun insertSongInHistory(historyEntity: HistoryEntity)
@Query("DELETE FROM HistoryEntity WHERE id= :songId")
fun deleteSongInHistory(songId: Long)
@Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1") @Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1")
suspend fun isSongPresentInHistory(songId: Long): HistoryEntity? suspend fun isSongPresentInHistory(songId: Long): HistoryEntity?

View File

@ -28,12 +28,6 @@ fun List<SongEntity>.toSongs(): List<Song> {
} }
} }
fun List<Song>.toSongs(playlistId: Long): List<SongEntity> {
return map {
it.toSongEntity(playlistId)
}
}
fun Song.toHistoryEntity(timePlayed: Long): HistoryEntity { fun Song.toHistoryEntity(timePlayed: Long): HistoryEntity {
return HistoryEntity( return HistoryEntity(
id = id, id = id,

View File

@ -60,6 +60,11 @@ class DeleteSongsDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
libraryViewModel = activity?.getViewModel() as LibraryViewModel libraryViewModel = activity?.getViewModel() as LibraryViewModel
val songs = extraNotNull<List<Song>>(EXTRA_SONG).value val songs = extraNotNull<List<Song>>(EXTRA_SONG).value
if (VersionUtils.hasR()) {
dismiss()
MusicUtil.deleteTracksR(requireActivity(), songs)
reloadTabs()
}
val pair = if (songs.size > 1) { val pair = if (songs.size > 1) {
Pair( Pair(
R.string.delete_songs_title, R.string.delete_songs_title,
@ -90,11 +95,7 @@ class DeleteSongsDialog : DialogFragment() {
if ((songs.size == 1) && MusicPlayerRemote.isPlaying(songs[0])) { if ((songs.size == 1) && MusicPlayerRemote.isPlaying(songs[0])) {
MusicPlayerRemote.playNextSong() MusicPlayerRemote.playNextSong()
} }
if (VersionUtils.hasR()) { if (!SAFUtil.isSAFRequiredForSongs(songs)) {
dismiss()
MusicUtil.deleteTracksR(requireActivity(), songs)
reloadTabs()
} else if (!SAFUtil.isSAFRequiredForSongs(songs)) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
dismiss() dismiss()
MusicUtil.deleteTracks(requireContext(), songs) MusicUtil.deleteTracks(requireContext(), songs)
@ -138,7 +139,7 @@ class DeleteSongsDialog : DialogFragment() {
} }
} }
fun reloadTabs() { private fun reloadTabs() {
libraryViewModel.forceReload(ReloadType.Songs) libraryViewModel.forceReload(ReloadType.Songs)
libraryViewModel.forceReload(ReloadType.HomeSections) libraryViewModel.forceReload(ReloadType.HomeSections)
libraryViewModel.forceReload(ReloadType.Artists) libraryViewModel.forceReload(ReloadType.Artists)

View File

@ -29,6 +29,7 @@ import android.widget.SeekBar
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.addAccentColor import code.name.monkey.retromusic.extensions.addAccentColor
import code.name.monkey.retromusic.extensions.colorButtons import code.name.monkey.retromusic.extensions.colorButtons
@ -138,7 +139,11 @@ class SleepTimerDialog : DialogFragment() {
} }
private fun makeTimerPendingIntent(flag: Int): PendingIntent? { private fun makeTimerPendingIntent(flag: Int): PendingIntent? {
return PendingIntent.getService(requireActivity(), 0, makeTimerIntent(), flag or PendingIntent.FLAG_IMMUTABLE) return PendingIntent.getService(
requireActivity(), 0, makeTimerIntent(), flag or if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
} }
private fun makeTimerIntent(): Intent { private fun makeTimerIntent(): Intent {

View File

@ -3,13 +3,9 @@ package code.name.monkey.retromusic.extensions
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Environment
import android.provider.DocumentsContract
import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import java.io.File
import java.io.OutputStream import java.io.OutputStream
fun Fragment.createNewFile( fun Fragment.createNewFile(

View File

@ -78,7 +78,7 @@ fun isBlack(fArr: FloatArray): Boolean {
fun isNearRedLine(fArr: FloatArray): Boolean { fun isNearRedLine(fArr: FloatArray): Boolean {
val f = fArr[0] val f = fArr[0]
return f >= 10.0f && f <= 37.0f && fArr[1] <= 0.82f return f in 10.0f..37.0f && fArr[1] <= 0.82f
} }
fun isDark(@ColorInt i: Int): Boolean { fun isDark(@ColorInt i: Int): Boolean {

View File

@ -0,0 +1,16 @@
package code.name.monkey.retromusic.fragments
import androidx.annotation.LayoutRes
import code.name.monkey.retromusic.R
enum class GridStyle constructor(
@param:LayoutRes @field:LayoutRes val layoutResId: Int,
val id: Int
) {
Grid(R.layout.item_grid, 0),
Card(R.layout.item_card, 1),
ColoredCard(R.layout.item_card_color, 2),
Circular(R.layout.item_grid_circle, 3),
Image(R.layout.image, 4),
GradientImage(R.layout.item_image_gradient, 5)
}

View File

@ -29,6 +29,7 @@ import code.name.monkey.retromusic.util.DensityUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File
class LibraryViewModel( class LibraryViewModel(
private val repository: RealRepository private val repository: RealRepository
@ -43,7 +44,7 @@ class LibraryViewModel(
private val legacyPlaylists = MutableLiveData<List<Playlist>>() private val legacyPlaylists = MutableLiveData<List<Playlist>>()
private val genres = MutableLiveData<List<Genre>>() private val genres = MutableLiveData<List<Genre>>()
private val searchResults = MutableLiveData<List<Any>>() private val searchResults = MutableLiveData<List<Any>>()
private val fabMargin = MutableLiveData<Int>(0) private val fabMargin = MutableLiveData(0)
val paletteColor: LiveData<Int> = _paletteColor val paletteColor: LiveData<Int> = _paletteColor
init { init {
@ -227,7 +228,7 @@ class LibraryViewModel(
repository.deleteRoomPlaylist(playlists) repository.deleteRoomPlaylist(playlists)
} }
suspend fun albumById(id: Long) = repository.albumById(id) fun albumById(id: Long) = repository.albumById(id)
suspend fun artistById(id: Long) = repository.artistById(id) suspend fun artistById(id: Long) = repository.artistById(id)
suspend fun favoritePlaylist() = repository.favoritePlaylist() suspend fun favoritePlaylist() = repository.favoritePlaylist()
suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song) suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song)
@ -272,6 +273,16 @@ class LibraryViewModel(
} }
fun playCountSongs(): LiveData<List<Song>> = liveData { fun playCountSongs(): LiveData<List<Song>> = liveData {
val songs = repository.playCountSongs().map {
it.toSong()
}
emit(songs)
// Cleaning up deleted or moved songs
songs.forEach { song ->
if (!File(song.data).exists() || song.id == -1L) {
repository.deleteSongInPlayCount(song.toPlayCount())
}
}
emit(repository.playCountSongs().map { emit(repository.playCountSongs().map {
it.toSong() it.toSong()
}) })
@ -303,7 +314,21 @@ class LibraryViewModel(
emit(repository.contributor()) emit(repository.contributor())
} }
fun observableHistorySongs() = repository.observableHistorySongs() fun observableHistorySongs(): LiveData<List<Song>> = liveData {
val songs = repository.historySong().map {
it.toSong()
}
emit(songs)
// Cleaning up deleted or moved songs
songs.forEach { song ->
if (!File(song.data).exists() || song.id == -1L) {
repository.deleteSongInHistory(song.id)
}
}
emit(repository.historySong().map {
it.toSong()
})
}
fun favorites() = repository.favorites() fun favorites() = repository.favorites()

View File

@ -405,9 +405,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
binding.albumCoverContainer, binding.albumCoverContainer,
"${getString(R.string.transition_album_art)}_${album.id}" "${getString(R.string.transition_album_art)}_${album.id}"
) )
startActivityForResult( startActivity(
intent, intent, options.toBundle()
TAG_EDITOR_REQUEST, options.toBundle()
) )
return true return true
} }
@ -499,8 +498,4 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
companion object {
const val TAG_EDITOR_REQUEST = 9002
}
} }

View File

@ -26,6 +26,7 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.extensions.navigate import code.name.monkey.retromusic.extensions.navigate
import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -131,11 +132,13 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
} }
override fun loadLayoutRes(): Int { override fun loadLayoutRes(): Int {
return PreferenceUtil.albumGridStyle return PreferenceUtil.albumGridStyle.layoutResId
} }
override fun saveLayoutRes(layoutRes: Int) { override fun saveLayoutRes(layoutRes: Int) {
PreferenceUtil.albumGridStyle = layoutRes PreferenceUtil.albumGridStyle = GridStyle.values().first { gridStyle ->
gridStyle.layoutResId == layoutRes
}
} }
companion object { companion object {
@ -307,9 +310,9 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
R.id.action_layout_circular -> R.layout.item_grid_circle R.id.action_layout_circular -> R.layout.item_grid_circle
R.id.action_layout_image -> R.layout.image R.id.action_layout_image -> R.layout.image
R.id.action_layout_gradient_image -> R.layout.item_image_gradient R.id.action_layout_gradient_image -> R.layout.item_image_gradient
else -> PreferenceUtil.albumGridStyle else -> PreferenceUtil.albumGridStyle.layoutResId
} }
if (layoutRes != PreferenceUtil.albumGridStyle) { if (layoutRes != PreferenceUtil.albumGridStyle.layoutResId) {
item.isChecked = true item.isChecked = true
setAndSaveLayoutRes(layoutRes) setAndSaveLayoutRes(layoutRes)
return true return true

View File

@ -25,8 +25,8 @@ import code.name.monkey.retromusic.EXTRA_ARTIST_ID
import code.name.monkey.retromusic.EXTRA_ARTIST_NAME import code.name.monkey.retromusic.EXTRA_ARTIST_NAME
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
import code.name.monkey.retromusic.extensions.navigate
import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -132,11 +132,13 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
} }
override fun loadLayoutRes(): Int { override fun loadLayoutRes(): Int {
return PreferenceUtil.artistGridStyle return PreferenceUtil.artistGridStyle.layoutResId
} }
override fun saveLayoutRes(layoutRes: Int) { override fun saveLayoutRes(layoutRes: Int) {
PreferenceUtil.artistGridStyle = layoutRes PreferenceUtil.artistGridStyle = GridStyle.values().first { gridStyle ->
gridStyle.layoutResId == layoutRes
}
} }
companion object { companion object {
@ -312,9 +314,9 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
R.id.action_layout_circular -> R.layout.item_grid_circle R.id.action_layout_circular -> R.layout.item_grid_circle
R.id.action_layout_image -> R.layout.image R.id.action_layout_image -> R.layout.image
R.id.action_layout_gradient_image -> R.layout.item_image_gradient R.id.action_layout_gradient_image -> R.layout.item_image_gradient
else -> PreferenceUtil.artistGridStyle else -> PreferenceUtil.artistGridStyle.layoutResId
} }
if (layoutRes != PreferenceUtil.artistGridStyle) { if (layoutRes != PreferenceUtil.artistGridStyle.layoutResId) {
item.isChecked = true item.isChecked = true
setAndSaveLayoutRes(layoutRes) setAndSaveLayoutRes(layoutRes)
return true return true

View File

@ -48,7 +48,9 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
backupViewModel.loadBackups() backupViewModel.loadBackups()
val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) { val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
backupViewModel.restoreBackup(requireActivity(), requireContext().contentResolver.openInputStream(it)) it?.let {
backupViewModel.restoreBackup(requireActivity(), requireContext().contentResolver.openInputStream(it))
}
} }
} }
binding.createBackup.setOnClickListener { binding.createBackup.setOnClickListener {
@ -70,7 +72,7 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
} }
private fun checkIsEmpty() { private fun checkIsEmpty() {
val isEmpty = backupAdapter!!.itemCount == 0 val isEmpty = backupAdapter?.itemCount == 0
binding.backupTitle.isVisible = !isEmpty binding.backupTitle.isVisible = !isEmpty
binding.backupRecyclerview.isVisible = !isEmpty binding.backupRecyclerview.isVisible = !isEmpty
} }

View File

@ -37,6 +37,7 @@ import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.navOptions
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
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
@ -151,7 +152,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return true return true
} }
R.id.action_show_lyrics -> { R.id.action_show_lyrics -> {
NavigationUtil.goToLyrics(requireActivity()) goToLyrics(requireActivity())
return true return true
} }
R.id.action_equalizer -> { R.id.action_equalizer -> {
@ -193,7 +194,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
private fun showLyricsIcon(item: MenuItem) { private fun showLyricsIcon(item: MenuItem) {
val icon = val icon =
if (PreferenceUtil.showLyrics) R.drawable.ic_lyrics else R.drawable.ic_lyrics_outline if (PreferenceUtil.showLyrics) R.drawable.ic_lyrics else R.drawable.ic_lyrics_outline
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable( val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
requireContext(), requireContext(),
icon, icon,
toolbarIconColor() toolbarIconColor()
@ -255,7 +256,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
} 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
} }
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable( val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
requireContext(), requireContext(),
icon, icon,
toolbarIconColor() toolbarIconColor()
@ -394,7 +395,11 @@ fun goToArtist(activity: Activity) {
findNavController(R.id.fragment_container).navigate( findNavController(R.id.fragment_container).navigate(
R.id.artistDetailsFragment, R.id.artistDetailsFragment,
bundleOf(EXTRA_ARTIST_ID to song.artistId) bundleOf(EXTRA_ARTIST_ID to song.artistId),
navOptions {
launchSingleTop = true
},
null
) )
} }
} }
@ -413,7 +418,29 @@ fun goToAlbum(activity: Activity) {
findNavController(R.id.fragment_container).navigate( findNavController(R.id.fragment_container).navigate(
R.id.albumDetailsFragment, R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to song.albumId) bundleOf(EXTRA_ALBUM_ID to song.albumId),
navOptions {
launchSingleTop = true
},
null
)
}
}
fun goToLyrics(activity: Activity) {
if (activity !is MainActivity) return
activity.apply {
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
setBottomNavVisibility(false)
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
collapsePanel()
}
findNavController(R.id.fragment_container).navigate(
R.id.lyrics_fragment,
null,
navOptions { launchSingleTop = true },
null
) )
} }
} }

View File

@ -41,7 +41,6 @@ import code.name.monkey.retromusic.adapter.StorageAdapter
import code.name.monkey.retromusic.adapter.StorageClickListener import code.name.monkey.retromusic.adapter.StorageClickListener
import code.name.monkey.retromusic.databinding.FragmentFolderBinding import code.name.monkey.retromusic.databinding.FragmentFolderBinding
import code.name.monkey.retromusic.extensions.drawNextToNavbar import code.name.monkey.retromusic.extensions.drawNextToNavbar
import code.name.monkey.retromusic.extensions.navigate
import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.fragments.folder.FoldersFragment.ListPathsAsyncTask.OnPathsListedCallback import code.name.monkey.retromusic.fragments.folder.FoldersFragment.ListPathsAsyncTask.OnPathsListedCallback
@ -725,9 +724,10 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
} }
abstract class ListingFilesDialogAsyncTask<Params, Progress, Result> : abstract class ListingFilesDialogAsyncTask<Params, Progress, Result> internal constructor(
DialogAsyncTask<Params, Progress, Result> { context: Context?
internal constructor(context: Context?) : super(context) ) :
DialogAsyncTask<Params, Progress, Result>(context) {
override fun createDialog(context: Context): Dialog { override fun createDialog(context: Context): Dialog {
return MaterialAlertDialogBuilder(context) return MaterialAlertDialogBuilder(context)

View File

@ -162,30 +162,26 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
class FlingPlayBackController(context: Context) : View.OnTouchListener { class FlingPlayBackController(context: Context) : View.OnTouchListener {
private var flingPlayBackController: GestureDetector private var flingPlayBackController = GestureDetector(context,
object : GestureDetector.SimpleOnGestureListener() {
init { override fun onFling(
flingPlayBackController = GestureDetector(context, e1: MotionEvent,
object : GestureDetector.SimpleOnGestureListener() { e2: MotionEvent,
override fun onFling( velocityX: Float,
e1: MotionEvent, velocityY: Float
e2: MotionEvent, ): Boolean {
velocityX: Float, if (abs(velocityX) > abs(velocityY)) {
velocityY: Float if (velocityX < 0) {
): Boolean { MusicPlayerRemote.playNextSong()
if (abs(velocityX) > abs(velocityY)) { return true
if (velocityX < 0) { } else if (velocityX > 0) {
MusicPlayerRemote.playNextSong() MusicPlayerRemote.playPreviousSong()
return true return true
} else if (velocityX > 0) {
MusicPlayerRemote.playPreviousSong()
return true
}
} }
return false
} }
}) return false
} }
})
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View, event: MotionEvent): Boolean { override fun onTouch(v: View, event: MotionEvent): Boolean {

View File

@ -61,7 +61,6 @@ import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException
class UserInfoFragment : Fragment() { class UserInfoFragment : Fragment() {
@ -218,13 +217,13 @@ class UserInfoFragment : Fragment() {
val appDir = requireContext().filesDir val appDir = requireContext().filesDir
val file = File(appDir, fileName) val file = File(appDir, fileName)
var successful = false var successful = false
try { kotlin.runCatching {
val os = BufferedOutputStream(FileOutputStream(file)) val os = BufferedOutputStream(FileOutputStream(file))
successful = ImageUtil.resizeBitmap(bitmap, 2048) successful = ImageUtil.resizeBitmap(bitmap, 2048)
.compress(Bitmap.CompressFormat.WEBP, 100, os) .compress(Bitmap.CompressFormat.WEBP, 100, os)
withContext(Dispatchers.IO) { os.close() } withContext(Dispatchers.IO) { os.close() }
} catch (e: IOException) { }.onFailure {
e.printStackTrace() it.printStackTrace()
} }
if (successful) { if (successful) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {

View File

@ -32,6 +32,7 @@ import code.name.monkey.retromusic.databinding.FragmentPlayerAlbumCoverBinding
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.AbsPlayerFragment
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
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
@ -39,7 +40,6 @@ 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.NavigationUtil
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
@ -214,7 +214,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} }
// Go to lyrics activity when clicked lyrics // Go to lyrics activity when clicked lyrics
binding.playerLyricsLine2.setOnClickListener { binding.playerLyricsLine2.setOnClickListener {
NavigationUtil.goToLyrics(requireActivity()) goToLyrics(requireActivity())
} }
} }

View File

@ -63,7 +63,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var audioVolumeObserver: AudioVolumeObserver? = null private var audioVolumeObserver: AudioVolumeObserver? = null
private val audioManager: AudioManager? private val audioManager: AudioManager
get() = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager get() = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
private var _binding: FragmentCirclePlayerBinding? = null private var _binding: FragmentCirclePlayerBinding? = null
@ -239,7 +239,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
override fun onProgressChanged(seekArc: SeekArc?, progress: Int, fromUser: Boolean) { override fun onProgressChanged(seekArc: SeekArc?, progress: Int, fromUser: Boolean) {
val audioManager = audioManager val audioManager = audioManager
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0) audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0)
} }
override fun onStartTrackingTouch(seekArc: SeekArc?) { override fun onStartTrackingTouch(seekArc: SeekArc?) {

View File

@ -247,7 +247,7 @@ class ClassicPlayerFragment : AbsPlayerFragment(R.layout.fragment_classic_player
updateQueue() updateQueue()
} }
override fun playerToolbar(): Toolbar? { override fun playerToolbar(): Toolbar {
return binding.playerToolbar return binding.playerToolbar
} }

View File

@ -316,7 +316,7 @@ class FullPlaybackControlsFragment :
fun updateIsFavorite(animate: Boolean = false) { fun updateIsFavorite(animate: Boolean = false) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist() val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
if (playlist != null) { if (playlist != null) {
val song: SongEntity = val song: SongEntity =
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId) MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
@ -327,7 +327,7 @@ class FullPlaybackControlsFragment :
} 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
} }
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable( val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
requireContext(), requireContext(),
icon, icon,
Color.WHITE Color.WHITE
@ -347,7 +347,7 @@ class FullPlaybackControlsFragment :
private fun toggleFavorite(song: Song) { private fun toggleFavorite(song: Song) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist() val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
if (playlist != null) { if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId) val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty() val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty()

View File

@ -281,7 +281,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private fun updateIsFavoriteIcon(animate: Boolean = false) { private fun updateIsFavoriteIcon(animate: Boolean = false) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist() val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
if (playlist != null) { if (playlist != null) {
val song: SongEntity = val song: SongEntity =
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId) MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)

View File

@ -25,12 +25,12 @@ import code.name.monkey.retromusic.EXTRA_PLAYLIST
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.extensions.navigate
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
@ -42,7 +42,7 @@ class PlaylistsFragment :
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
libraryViewModel.getPlaylists().observe(viewLifecycleOwner, { libraryViewModel.getPlaylists().observe(viewLifecycleOwner, {
if (it.isNotEmpty()) if (it.isNotEmpty())
adapter?.swapDataSet(it) adapter?.swapDataSet(it.filter { playlistWithSongs-> playlistWithSongs.songs.isNotEmpty() })
else else
adapter?.swapDataSet(listOf()) adapter?.swapDataSet(listOf())
}) })
@ -66,9 +66,10 @@ class PlaylistsFragment :
} }
override fun createAdapter(): PlaylistAdapter { override fun createAdapter(): PlaylistAdapter {
val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet
return PlaylistAdapter( return PlaylistAdapter(
requireActivity(), requireActivity(),
ArrayList(), dataSet,
itemLayoutRes(), itemLayoutRes(),
null, null,
this this
@ -77,7 +78,11 @@ class PlaylistsFragment :
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
menu.removeItem(R.id.action_grid_size) val gridSizeItem: MenuItem = menu.findItem(R.id.action_grid_size)
if (RetroUtil.isLandscape()) {
gridSizeItem.setTitle(R.string.action_grid_size_land)
}
setupGridSizeMenu(gridSizeItem.subMenu)
menu.removeItem(R.id.action_layout_type) menu.removeItem(R.id.action_layout_type)
menu.add(0, R.id.action_add_to_playlist, 0, R.string.new_playlist_title) menu.add(0, R.id.action_add_to_playlist, 0, R.string.new_playlist_title)
menu.add(0, R.id.action_import_playlist, 0, R.string.import_playlist) menu.add(0, R.id.action_import_playlist, 0, R.string.import_playlist)
@ -89,12 +94,46 @@ class PlaylistsFragment :
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (handleGridSizeMenuItem(item)) {
return true
}
if (handleSortOrderMenuItem(item)) { if (handleSortOrderMenuItem(item)) {
return true return true
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
private fun setupGridSizeMenu(gridSizeMenu: SubMenu) {
when (getGridSize()) {
1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true
2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true
3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true
4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true
5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true
6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true
7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true
8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true
}
val gridSize = if (RetroUtil.isLandscape()) 4 else 2
if (gridSize < 8) {
gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false
}
if (gridSize < 7) {
gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false
}
if (gridSize < 6) {
gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false
}
if (gridSize < 5) {
gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false
}
if (gridSize < 4) {
gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false
}
if (gridSize < 3) {
gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false
}
}
private fun setUpSortOrderMenu(subMenu: SubMenu) { private fun setUpSortOrderMenu(subMenu: SubMenu) {
val order: String? = getSortOrder() val order: String? = getSortOrder()
@ -142,13 +181,32 @@ class PlaylistsFragment :
return false return false
} }
private fun handleGridSizeMenuItem(item: MenuItem): Boolean {
val gridSize = when (item.itemId) {
R.id.action_grid_size_1 -> 1
R.id.action_grid_size_2 -> 2
R.id.action_grid_size_3 -> 3
R.id.action_grid_size_4 -> 4
R.id.action_grid_size_5 -> 5
R.id.action_grid_size_6 -> 6
R.id.action_grid_size_7 -> 7
R.id.action_grid_size_8 -> 8
else -> 0
}
if (gridSize > 0) {
item.isChecked = true
setAndSaveGridSize(gridSize)
return true
}
return false
}
private fun createId(menu: SubMenu, id: Int, title: Int, checked: Boolean) { private fun createId(menu: SubMenu, id: Int, title: Int, checked: Boolean) {
menu.add(0, id, 0, title).isChecked = checked menu.add(0, id, 0, title).isChecked = checked
} }
override fun setGridSize(gridSize: Int) { override fun setGridSize(gridSize: Int) {
TODO("Not yet implemented") adapter?.notifyDataSetChanged()
} }
override fun setSortOrder(sortOrder: String) { override fun setSortOrder(sortOrder: String) {
@ -164,23 +222,23 @@ class PlaylistsFragment :
} }
override fun loadGridSize(): Int { override fun loadGridSize(): Int {
return 2 return PreferenceUtil.playlistGridSize
} }
override fun saveGridSize(gridColumns: Int) { override fun saveGridSize(gridColumns: Int) {
//Add grid save PreferenceUtil.playlistGridSize = gridColumns
} }
override fun loadGridSizeLand(): Int { override fun loadGridSizeLand(): Int {
return 4 return PreferenceUtil.playlistGridSizeLand
} }
override fun saveGridSizeLand(gridColumns: Int) { override fun saveGridSizeLand(gridColumns: Int) {
//Add land grid save PreferenceUtil.playlistGridSizeLand = gridColumns
} }
override fun loadLayoutRes(): Int { override fun loadLayoutRes(): Int {
return R.layout.item_card return R.layout.item_grid
} }
override fun saveLayoutRes(layoutRes: Int) { override fun saveLayoutRes(layoutRes: Int) {

View File

@ -21,8 +21,8 @@ import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.extensions.navigate
import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -114,11 +114,13 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
@LayoutRes @LayoutRes
override fun loadLayoutRes(): Int { override fun loadLayoutRes(): Int {
return PreferenceUtil.songGridStyle return PreferenceUtil.songGridStyle.layoutResId
} }
override fun saveLayoutRes(@LayoutRes layoutRes: Int) { override fun saveLayoutRes(@LayoutRes layoutRes: Int) {
PreferenceUtil.songGridStyle = layoutRes PreferenceUtil.songGridStyle = GridStyle.values().first { gridStyle ->
gridStyle.layoutResId == layoutRes
}
} }
override fun setSortOrder(sortOrder: String) { override fun setSortOrder(sortOrder: String) {
@ -307,9 +309,9 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
R.id.action_layout_circular -> R.layout.item_grid_circle R.id.action_layout_circular -> R.layout.item_grid_circle
R.id.action_layout_image -> R.layout.image R.id.action_layout_image -> R.layout.image
R.id.action_layout_gradient_image -> R.layout.item_image_gradient R.id.action_layout_gradient_image -> R.layout.item_image_gradient
else -> PreferenceUtil.songGridStyle else -> PreferenceUtil.songGridStyle.layoutResId
} }
if (layoutRes != PreferenceUtil.songGridStyle) { if (layoutRes != PreferenceUtil.songGridStyle.layoutResId) {
item.isChecked = true item.isChecked = true
setAndSaveLayoutRes(layoutRes) setAndSaveLayoutRes(layoutRes)
return true return true

View File

@ -17,9 +17,7 @@ class PlaylistPreviewFetcher(val context: Context, private val playlistPreview:
val bitmap = val bitmap =
AutoGeneratedPlaylistBitmap.getBitmap( AutoGeneratedPlaylistBitmap.getBitmap(
context, context,
playlistPreview.songs.shuffled(), playlistPreview.songs.shuffled()
true,
false
) )
callback.onDataReady(bitmap) callback.onDataReady(bitmap)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -23,28 +23,33 @@ object BackupHelper {
zipItems.addAll(getDatabaseZipItems(context)) zipItems.addAll(getDatabaseZipItems(context))
zipItems.addAll(getSettingsZipItems(context)) zipItems.addAll(getSettingsZipItems(context))
getUserImageZipItems(context)?.let { zipItems.addAll(it) } getUserImageZipItems(context)?.let { zipItems.addAll(it) }
withContext(Dispatchers.IO) { zipItems.addAll(getCustomArtistZipItems(context))
zipAll(zipItems, backupFile) zipAll(zipItems, backupFile)
}
} }
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) { private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) {
try { withContext(Dispatchers.IO) {
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out -> kotlin.runCatching {
for (zipItem in zipItems) { ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
FileInputStream(zipItem.filePath).use { fi -> for (zipItem in zipItems) {
BufferedInputStream(fi).use { origin -> FileInputStream(zipItem.filePath).use { fi ->
val entry = ZipEntry(zipItem.zipPath) BufferedInputStream(fi).use { origin ->
out.putNextEntry(entry) val entry = ZipEntry(zipItem.zipPath)
origin.copyTo(out) out.putNextEntry(entry)
origin.copyTo(out)
}
} }
} }
} }
}.onFailure {
it.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
.show()
}
} }
} catch (exception: FileNotFoundException) {
exception.printStackTrace()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT) Toast.makeText(App.getContext(), "Backup created successfully", Toast.LENGTH_SHORT)
.show() .show()
} }
} }
@ -76,6 +81,28 @@ object BackupHelper {
} }
} }
private fun getCustomArtistZipItems(context: Context): List<ZipItem> {
val zipItemList = mutableListOf<ZipItem>()
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/"
zipItemList.addAll(
File(context.filesDir, "custom_artist_images")
.listFiles()?.map {
ZipItem(
it.absolutePath,
"$CUSTOM_ARTISTS_PATH${File.separator}custom_artist_images${File.separator}${it.name}"
)
}?.toList() ?: listOf()
)
zipItemList.add(
ZipItem(
sharedPrefPath + File.separator + "custom_artist_image.xml",
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml"
)
)
return zipItemList
}
suspend fun restoreBackup(context: Context, inputStream: InputStream?) { suspend fun restoreBackup(context: Context, inputStream: InputStream?) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
ZipInputStream(inputStream).use { ZipInputStream(inputStream).use {
@ -84,6 +111,16 @@ object BackupHelper {
if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry) if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry)
if (entry.isPreferenceEntry()) restorePreferences(context, it, entry) if (entry.isPreferenceEntry()) restorePreferences(context, it, entry)
if (entry.isImageEntry()) restoreImages(context, it, entry) if (entry.isImageEntry()) restoreImages(context, it, entry)
if (entry.isCustomArtistImageEntry()) restoreCustomArtistImages(
context,
it,
entry
)
if (entry.isCustomArtistPrefEntry()) restoreCustomArtistPrefs(
context,
it,
entry
)
entry = it.nextEntry entry = it.nextEntry
} }
} }
@ -133,6 +170,48 @@ object BackupHelper {
} }
} }
private fun restoreCustomArtistImages(
context: Context,
zipIn: ZipInputStream,
zipEntry: ZipEntry
) {
val parentFolder = File(context.filesDir, "custom_artist_images")
if (!parentFolder.exists()) {
parentFolder.mkdirs()
}
BufferedOutputStream(
FileOutputStream(
File(
parentFolder,
zipEntry.getFileName()
)
)
).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
private fun restoreCustomArtistPrefs(
context: Context,
zipIn: ZipInputStream,
zipEntry: ZipEntry
) {
val filePath =
context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + zipEntry.getFileName()
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
val backupRootPath = val backupRootPath =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
.toString() + "/RetroMusic/Backups/" .toString() + "/RetroMusic/Backups/"
@ -141,6 +220,7 @@ object BackupHelper {
private const val DATABASES_PATH = "databases" private const val DATABASES_PATH = "databases"
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 THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]" private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
private fun ZipEntry.isDatabaseEntry(): Boolean { private fun ZipEntry.isDatabaseEntry(): Boolean {
@ -155,6 +235,14 @@ object BackupHelper {
return name.startsWith(IMAGES_PATH) return name.startsWith(IMAGES_PATH)
} }
private fun ZipEntry.isCustomArtistImageEntry(): Boolean {
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("custom_artist_images")
}
private fun ZipEntry.isCustomArtistPrefEntry(): Boolean {
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
}
private fun ZipEntry.getFileName(): String { private fun ZipEntry.getFileName(): String {
return name.substring(name.lastIndexOf(File.separator)) return name.substring(name.lastIndexOf(File.separator))
} }

View File

@ -26,7 +26,7 @@ object M3UWriter : M3UConstants {
fun write( fun write(
dir: File, dir: File,
playlist: Playlist playlist: Playlist
): File? { ): File {
if (!dir.exists()) dir.mkdirs() if (!dir.exists()) dir.mkdirs()
val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION) val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION)
val songs = playlist.getSongs() val songs = playlist.getSongs()

View File

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.helper
import android.os.Handler import android.os.Handler
import android.os.Message import android.os.Message
import kotlin.math.max
class MusicProgressViewUpdateHelper : Handler { class MusicProgressViewUpdateHelper : Handler {
@ -62,7 +63,7 @@ class MusicProgressViewUpdateHelper : Handler {
val remainingMillis = intervalPlaying - progressMillis % intervalPlaying val remainingMillis = intervalPlaying - progressMillis % intervalPlaying
return Math.max(MIN_INTERVAL, remainingMillis) return max(MIN_INTERVAL, remainingMillis)
} }
private fun queueNextRefresh(delay: Long) { private fun queueNextRefresh(delay: Long) {

View File

@ -134,7 +134,7 @@ class SortOrder {
companion object { companion object {
/* Artist song sort order A-Z */ /* Artist song sort order A-Z */
const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER private 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

@ -76,8 +76,8 @@ public class StackBlur {
original.getPixels(currentPixels, 0, w, 0, 0, w, h); original.getPixels(currentPixels, 0, w, 0, 0, w, h);
int cores = EXECUTOR_THREADS; int cores = EXECUTOR_THREADS;
ArrayList<BlurTask> horizontal = new ArrayList<BlurTask>(cores); ArrayList<BlurTask> horizontal = new ArrayList<>(cores);
ArrayList<BlurTask> vertical = new ArrayList<BlurTask>(cores); ArrayList<BlurTask> vertical = new ArrayList<>(cores);
for (int i = 0; i < cores; i++) { for (int i = 0; i < cores; i++) {
horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1)); horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2)); vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2));
@ -158,7 +158,7 @@ public class StackBlur {
for (x = 0; x < w; x++) { for (x = 0; x < w; x++) {
src[dst_i] = src[dst_i] =
(int) (int)
((src[dst_i] & 0xFFFFFFFF) ((src[dst_i])
| ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) | ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16)
| ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) | ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8)
| ((((sum_b * mul_sum) >>> shr_sum) & 0xff))); | ((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
@ -245,7 +245,7 @@ public class StackBlur {
for (y = 0; y < h; y++) { for (y = 0; y < h; y++) {
src[dst_i] = src[dst_i] =
(int) (int)
((src[dst_i] & 0xFFFFFFFF) ((src[dst_i])
| ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) | ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16)
| ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) | ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8)
| ((((sum_b * mul_sum) >>> shr_sum) & 0xff))); | ((((sum_b * mul_sum) >>> shr_sum) & 0xff)));

View File

@ -1,138 +0,0 @@
package code.name.monkey.retromusic.lyrics;
import android.content.Context;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Desc : Author : Lauzy Date : 2017/10/13 Blog : http://www.jianshu.com/u/e76853f863a9 Email :
* freedompaladin@gmail.com
*/
public class LrcHelper {
private static final String CHARSET = "utf-8";
// [03:56.00][03:18.00][02:06.00][01:07.00]原谅我这一生不羁放纵爱自由
private static final String LINE_REGEX = "((\\[\\d{2}:\\d{2}\\.\\d{2}])+)(.*)";
private static final String TIME_REGEX = "\\[(\\d{2}):(\\d{2})\\.(\\d{2})]";
public static List<Lrc> parseLrcFromAssets(Context context, String fileName) {
try {
return parseInputStream(context.getResources().getAssets().open(fileName));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static List<Lrc> parseLrcFromFile(File file) {
try {
return parseInputStream(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
private static List<Lrc> parseInputStream(InputStream inputStream) {
List<Lrc> lrcs = new ArrayList<>();
InputStreamReader isr = null;
BufferedReader br = null;
try {
isr = new InputStreamReader(inputStream, CHARSET);
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
List<Lrc> lrcList = parseLrc(line);
if (lrcList != null && lrcList.size() != 0) {
lrcs.addAll(lrcList);
}
}
sortLrcs(lrcs);
return lrcs;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (isr != null) {
isr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
return lrcs;
}
private static void sortLrcs(List<Lrc> lrcs) {
Collections.sort(
lrcs,
new Comparator<Lrc>() {
@Override
public int compare(Lrc o1, Lrc o2) {
return (int) (o1.getTime() - o2.getTime());
}
});
}
private static List<Lrc> parseLrc(String lrcLine) {
if (lrcLine.trim().isEmpty()) {
return null;
}
List<Lrc> lrcs = new ArrayList<>();
Matcher matcher = Pattern.compile(LINE_REGEX).matcher(lrcLine);
if (!matcher.matches()) {
return null;
}
String time = matcher.group(1);
String content = matcher.group(3);
Matcher timeMatcher = Pattern.compile(TIME_REGEX).matcher(time);
while (timeMatcher.find()) {
String min = timeMatcher.group(1);
String sec = timeMatcher.group(2);
String mil = timeMatcher.group(3);
Lrc lrc = new Lrc();
if (content != null && content.length() != 0) {
lrc.setTime(
Long.parseLong(min) * 60 * 1000
+ Long.parseLong(sec) * 1000
+ Long.parseLong(mil) * 10);
lrc.setText(content);
lrcs.add(lrc);
}
}
return lrcs;
}
public static String formatTime(long time) {
int min = (int) (time / 60000);
int sec = (int) (time / 1000 % 60);
return adjustFormat(min) + ":" + adjustFormat(sec);
}
private static String adjustFormat(int time) {
if (time < 10) {
return "0" + time;
}
return time + "";
}
}

View File

@ -1,59 +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.misc
import com.google.android.material.appbar.AppBarLayout
import kotlin.math.abs
/**
* @author Hemanth S (h4h13).
* https://stackoverflow.com/a/33891727
*/
abstract class AppBarStateChangeListener : AppBarLayout.OnOffsetChangedListener {
private var mCurrentState = State.IDLE
override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) {
when {
i == 0 -> {
if (mCurrentState != State.EXPANDED) {
onStateChanged(appBarLayout, State.EXPANDED)
}
mCurrentState = State.EXPANDED
}
abs(i) >= appBarLayout.totalScrollRange -> {
if (mCurrentState != State.COLLAPSED) {
onStateChanged(appBarLayout, State.COLLAPSED)
}
mCurrentState = State.COLLAPSED
}
else -> {
if (mCurrentState != State.IDLE) {
onStateChanged(appBarLayout, State.IDLE)
}
mCurrentState = State.IDLE
}
}
}
abstract fun onStateChanged(appBarLayout: AppBarLayout, state: State)
enum class State {
EXPANDED,
COLLAPSED,
IDLE
}
}

View File

@ -78,8 +78,8 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
private final FragmentManager mFragmentManager; private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null; private FragmentTransaction mCurTransaction = null;
private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>(); private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>(); private final ArrayList<Fragment> mFragments = new ArrayList<>();
private Fragment mCurrentPrimaryItem = null; private Fragment mCurrentPrimaryItem = null;
public CustomFragmentStatePagerAdapter(FragmentManager fm) { public CustomFragmentStatePagerAdapter(FragmentManager fm) {
@ -92,11 +92,11 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
public abstract Fragment getItem(int position); public abstract Fragment getItem(int position);
@Override @Override
public void startUpdate(ViewGroup container) {} public void startUpdate(@NonNull ViewGroup container) {}
@NonNull @NonNull
@Override @Override
public Object instantiateItem(ViewGroup container, int position) { public Object instantiateItem(@NonNull ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing // If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager // to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already // from its saved state, where the fragment manager has already
@ -132,7 +132,7 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
} }
@Override @Override
public void destroyItem(ViewGroup container, int position, Object object) { public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment) object; Fragment fragment = (Fragment) object;
if (mCurTransaction == null) { if (mCurTransaction == null) {
@ -152,23 +152,21 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
} }
@Override @Override
public void setPrimaryItem(ViewGroup container, int position, Object object) { public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment) object; Fragment fragment = (Fragment) object;
if (fragment != mCurrentPrimaryItem) { if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) { if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false); mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false); mCurrentPrimaryItem.setUserVisibleHint(false);
} }
if (fragment != null) { fragment.setMenuVisibility(true);
fragment.setMenuVisibility(true); fragment.setUserVisibleHint(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment; mCurrentPrimaryItem = fragment;
} }
} }
@Override @Override
public void finishUpdate(ViewGroup container) { public void finishUpdate(@NonNull ViewGroup container) {
if (mCurTransaction != null) { if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss(); mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null; mCurTransaction = null;
@ -177,7 +175,7 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
} }
@Override @Override
public boolean isViewFromObject(View view, Object object) { public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return ((Fragment) object).getView() == view; return ((Fragment) object).getView() == view;
} }
@ -212,8 +210,8 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
mSavedState.clear(); mSavedState.clear();
mFragments.clear(); mFragments.clear();
if (fss != null) { if (fss != null) {
for (int i = 0; i < fss.length; i++) { for (Parcelable parcelable : fss) {
mSavedState.add((Fragment.SavedState) fss[i]); mSavedState.add((Fragment.SavedState) parcelable);
} }
} }
Iterable<String> keys = bundle.keySet(); Iterable<String> keys = bundle.keySet();

View File

@ -40,8 +40,6 @@ abstract class WrappedAsyncTaskLoader<D>
if (!isReset) { if (!isReset) {
this.mData = data this.mData = data
super.deliverResult(data) super.deliverResult(data)
} else {
// An asynchronous query came in while the loader is stopped
} }
} }

View File

@ -148,7 +148,7 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
return view === instace return view === instace
} }
override fun getPageTitle(position: Int): CharSequence? { override fun getPageTitle(position: Int): CharSequence {
return context.getString(values()[position].titleRes) return context.getString(values()[position].titleRes)
} }
} }

View File

@ -18,6 +18,7 @@ import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.AttributeSet import android.util.AttributeSet
import androidx.appcompat.app.AlertDialog
import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN import androidx.core.graphics.BlendModeCompat.SRC_IN
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
@ -26,6 +27,7 @@ import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreferenc
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.dialogs.BlacklistFolderChooserDialog import code.name.monkey.retromusic.dialogs.BlacklistFolderChooserDialog
import code.name.monkey.retromusic.extensions.accentTextColor
import code.name.monkey.retromusic.extensions.colorButtons import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.colorControlNormal import code.name.monkey.retromusic.extensions.colorControlNormal
import code.name.monkey.retromusic.extensions.materialDialog import code.name.monkey.retromusic.extensions.materialDialog
@ -69,11 +71,7 @@ class BlacklistPreferenceDialog : DialogFragment(), BlacklistFolderChooserDialog
.setNeutralButton(R.string.clear_action) { _, _ -> .setNeutralButton(R.string.clear_action) { _, _ ->
materialDialog(R.string.clear_blacklist) materialDialog(R.string.clear_blacklist)
.setMessage(R.string.do_you_want_to_clear_the_blacklist) .setMessage(R.string.do_you_want_to_clear_the_blacklist)
.setPositiveButton(R.string.clear_action) { _, _ -> .setPositiveButton(R.string.clear_action, null)
BlacklistStore.getInstance(
requireContext()
).clear()
}
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.create() .create()
.colorButtons() .colorButtons()
@ -107,7 +105,21 @@ class BlacklistPreferenceDialog : DialogFragment(), BlacklistFolderChooserDialog
.colorButtons() .colorButtons()
.show() .show()
} }
.create().colorButtons() .create().apply {
setOnShowListener {
getButton(AlertDialog.BUTTON_POSITIVE).accentTextColor()
getButton(AlertDialog.BUTTON_NEGATIVE).accentTextColor()
getButton(AlertDialog.BUTTON_NEUTRAL).apply {
accentTextColor()
setOnClickListener {
BlacklistStore.getInstance(
requireContext()
).clear()
dismiss()
}
}
}
}
} }
private lateinit var paths: ArrayList<String> private lateinit var paths: ArrayList<String>

View File

@ -153,7 +153,7 @@ private class NowPlayingScreenAdapter(private val context: Context) : PagerAdapt
return view === instance return view === instance
} }
override fun getPageTitle(position: Int): CharSequence? { override fun getPageTitle(position: Int): CharSequence {
return context.getString(values()[position].titleRes) return context.getString(values()[position].titleRes)
} }
} }

View File

@ -84,30 +84,23 @@ public class HistoryStore extends SQLiteOpenHelper {
database.insert(RecentStoreColumns.NAME, null, values); database.insert(RecentStoreColumns.NAME, null, values);
// if our db is too large, delete the extra items // if our db is too large, delete the extra items
Cursor oldest = null; try (Cursor oldest = database.query(
try { RecentStoreColumns.NAME,
oldest = new String[]{RecentStoreColumns.TIME_PLAYED},
database.query( null,
RecentStoreColumns.NAME, null,
new String[] {RecentStoreColumns.TIME_PLAYED}, null,
null, null,
null, RecentStoreColumns.TIME_PLAYED + " ASC")) {
null,
null,
RecentStoreColumns.TIME_PLAYED + " ASC");
if (oldest != null && oldest.getCount() > MAX_ITEMS_IN_DB) { if (oldest != null && oldest.getCount() > MAX_ITEMS_IN_DB) {
oldest.moveToPosition(oldest.getCount() - MAX_ITEMS_IN_DB); oldest.moveToPosition(oldest.getCount() - MAX_ITEMS_IN_DB);
long timeOfRecordToKeep = oldest.getLong(0); long timeOfRecordToKeep = oldest.getLong(0);
database.delete( database.delete(
RecentStoreColumns.NAME, RecentStoreColumns.NAME,
RecentStoreColumns.TIME_PLAYED + " < ?", RecentStoreColumns.TIME_PLAYED + " < ?",
new String[] {String.valueOf(timeOfRecordToKeep)}); new String[]{String.valueOf(timeOfRecordToKeep)});
}
} finally {
if (oldest != null) {
oldest.close();
} }
} }
} finally { } finally {

View File

@ -40,14 +40,11 @@ public class SongPlayCountStore extends SQLiteOpenHelper {
@NonNull @NonNull
private static final Interpolator sInterpolator = new AccelerateInterpolator(1.5f); private static final Interpolator sInterpolator = new AccelerateInterpolator(1.5f);
// how high to multiply the interpolation curve // how high to multiply the interpolation curve
@SuppressWarnings("FieldCanBeLocal")
private static final int INTERPOLATOR_HEIGHT = 50; private static final int INTERPOLATOR_HEIGHT = 50;
// how high the base value is. The ratio of the Height to Base is what really matters // how high the base value is. The ratio of the Height to Base is what really matters
@SuppressWarnings("FieldCanBeLocal")
private static final int INTERPOLATOR_BASE = 25; private static final int INTERPOLATOR_BASE = 25;
@SuppressWarnings("FieldCanBeLocal")
private static final int ONE_WEEK_IN_MS = 1000 * 60 * 60 * 24 * 7; private static final int ONE_WEEK_IN_MS = 1000 * 60 * 60 * 24 * 7;
@NonNull @NonNull
@ -257,7 +254,7 @@ public class SongPlayCountStore extends SQLiteOpenHelper {
for (int i = 0; i < NUM_WEEKS - weekDiff; i++) { for (int i = 0; i < NUM_WEEKS - weekDiff; i++) {
playCounts[i + weekDiff] = cursor.getInt(getColumnIndexForWeek(i)); playCounts[i + weekDiff] = cursor.getInt(getColumnIndexForWeek(i));
} }
} else if (weekDiff < 0) { } else {
// time is shifted backwards (by user) - nor typical behavior but we // time is shifted backwards (by user) - nor typical behavior but we
// will still handle it // will still handle it

View File

@ -15,6 +15,7 @@
package code.name.monkey.retromusic.repository package code.name.monkey.retromusic.repository
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
import code.name.monkey.retromusic.ALBUM_ARTIST
import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
@ -110,7 +111,8 @@ class RealArtistRepository(
songRepository.makeSongCursor( songRepository.makeSongCursor(
null, null,
null, null,
getSongLoaderSortOrder() "lower($ALBUM_ARTIST)" +
if (PreferenceUtil.artistSortOrder == SortOrder.ArtistSortOrder.ARTIST_A_Z) "" else " DESC"
) )
) )
return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs)) return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs))
@ -155,12 +157,6 @@ class RealArtistRepository(
} else { } else {
Artist.empty Artist.empty
} }
}.apply {
if (PreferenceUtil.artistSortOrder == SortOrder.ArtistSortOrder.ARTIST_A_Z) {
sortedBy { it.name.lowercase() }
} else {
sortedByDescending { it.name.lowercase() }
}
} }
} }

View File

@ -97,6 +97,7 @@ interface Repository {
suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInHistory(songId: Long)
suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity>
suspend fun playCountSongs(): List<PlayCountEntity> suspend fun playCountSongs(): List<PlayCountEntity>
suspend fun blackListPaths(): List<BlackListStoreEntity> suspend fun blackListPaths(): List<BlackListStoreEntity>
@ -323,6 +324,9 @@ class RealRepository(
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) = override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
roomRepository.deleteSongInPlayCount(playCountEntity) roomRepository.deleteSongInPlayCount(playCountEntity)
override suspend fun deleteSongInHistory(songId: Long) =
roomRepository.deleteSongInHistory(songId)
override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> = override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> =
roomRepository.checkSongExistInPlayCount(songId) roomRepository.checkSongExistInPlayCount(songId)

View File

@ -36,6 +36,7 @@ interface RoomRepository {
suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInHistory(songId: Long)
suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity>
suspend fun playCountSongs(): List<PlayCountEntity> suspend fun playCountSongs(): List<PlayCountEntity>
suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>) suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>)
@ -161,6 +162,10 @@ class RealRoomRepository(
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) = override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
playCountDao.deleteSongInPlayCount(playCountEntity) playCountDao.deleteSongInPlayCount(playCountEntity)
override suspend fun deleteSongInHistory(songId: Long) {
historyDao.deleteSongInHistory(songId)
}
override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> = override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> =
playCountDao.checkSongExistInPlayCount(songId) playCountDao.checkSongExistInPlayCount(songId)

View File

@ -75,7 +75,7 @@ class RealTopPlayedRepository(
val notRecentlyPlayedSongs = songRepository.songs( val notRecentlyPlayedSongs = songRepository.songs(
makeNotRecentTracksCursorAndClearUpDatabase() makeNotRecentTracksCursorAndClearUpDatabase()
) )
allSongs.removeAll(playedSongs) allSongs.removeAll(playedSongs.toSet())
allSongs.addAll(notRecentlyPlayedSongs) allSongs.addAll(notRecentlyPlayedSongs)
return allSongs return allSongs
} }

View File

@ -75,6 +75,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Random; import java.util.Random;
import code.name.monkey.appthemehelper.util.VersionUtils;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.activities.LockScreenActivity; import code.name.monkey.retromusic.activities.LockScreenActivity;
import code.name.monkey.retromusic.appwidgets.AppWidgetBig; import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
@ -245,7 +246,6 @@ public class MusicService extends MediaBrowserServiceCompat
private List<Song> originalPlayingQueue = new ArrayList<>(); private List<Song> originalPlayingQueue = new ArrayList<>();
private List<Song> playingQueue = new ArrayList<>(); private List<Song> playingQueue = new ArrayList<>();
private boolean pausedByTransientLossOfFocus; private boolean pausedByTransientLossOfFocus;
private AudioVolumeObserver audioVolumeObserver = null;
private final BroadcastReceiver becomingNoisyReceiver = private final BroadcastReceiver becomingNoisyReceiver =
new BroadcastReceiver() { new BroadcastReceiver() {
@ -453,7 +453,7 @@ public class MusicService extends MediaBrowserServiceCompat
.registerContentObserver( .registerContentObserver(
MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver); MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
audioVolumeObserver = new AudioVolumeObserver(this); AudioVolumeObserver audioVolumeObserver = new AudioVolumeObserver(this);
audioVolumeObserver.register(AudioManager.STREAM_MUSIC, this); audioVolumeObserver.register(AudioManager.STREAM_MUSIC, this);
PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this); PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this);
@ -837,8 +837,7 @@ public class MusicService extends MediaBrowserServiceCompat
// Request from an untrusted package: return an empty browser root // Request from an untrusted package: return an empty browser root
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null); return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null);
} else { } else {
/** /** By default return the browsable root. Treat the EXTRA_RECENT flag as a special case
* By default return the browsable root. Treat the EXTRA_RECENT flag as a special case
* and return the recent root instead. * and return the recent root instead.
*/ */
boolean isRecentRequest = false; boolean isRecentRequest = false;
@ -1157,7 +1156,7 @@ public class MusicService extends MediaBrowserServiceCompat
playback.setNextDataSource(getTrackUri(Objects.requireNonNull(getSongAt(nextPosition)))); playback.setNextDataSource(getTrackUri(Objects.requireNonNull(getSongAt(nextPosition))));
} }
this.nextPosition = nextPosition; this.nextPosition = nextPosition;
} catch (Exception e) { } catch (Exception ignored) {
} }
} }
} }
@ -1350,9 +1349,7 @@ public class MusicService extends MediaBrowserServiceCompat
.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.getYear()) .putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.getYear())
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null); .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size());
metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size());
}
if (PreferenceUtil.INSTANCE.isAlbumArtOnLockScreen()) { if (PreferenceUtil.INSTANCE.isAlbumArtOnLockScreen()) {
final Point screenSize = RetroUtil.getScreenSize(MusicService.this); final Point screenSize = RetroUtil.getScreenSize(MusicService.this);
@ -1595,11 +1592,8 @@ public class MusicService extends MediaBrowserServiceCompat
mediaButtonIntent.setComponent(mediaButtonReceiverComponentName); mediaButtonIntent.setComponent(mediaButtonReceiverComponentName);
PendingIntent mediaButtonReceiverPendingIntent; PendingIntent mediaButtonReceiverPendingIntent;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent,
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, PendingIntent.FLAG_MUTABLE); VersionUtils.INSTANCE.hasMarshmallow() ? PendingIntent.FLAG_IMMUTABLE : 0);
} else {
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
}
mediaSession = new MediaSessionCompat( mediaSession = new MediaSessionCompat(
this, this,
@ -1608,9 +1602,6 @@ public class MusicService extends MediaBrowserServiceCompat
mediaButtonReceiverPendingIntent); mediaButtonReceiverPendingIntent);
MediaSessionCallback mediasessionCallback = MediaSessionCallback mediasessionCallback =
new MediaSessionCallback(getApplicationContext(), this); new MediaSessionCallback(getApplicationContext(), this);
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(mediasessionCallback); mediaSession.setCallback(mediasessionCallback);
mediaSession.setActive(true); mediaSession.setActive(true);
mediaSession.setMediaButtonReceiver(mediaButtonReceiverPendingIntent); mediaSession.setMediaButtonReceiver(mediaButtonReceiverPendingIntent);

View File

@ -38,6 +38,6 @@ class ThrottledSeekHandler(
companion object { companion object {
// milliseconds to throttle before calling run() to aggregate events // milliseconds to throttle before calling run() to aggregate events
private val THROTTLE: Long = 500 private const val THROTTLE: Long = 500
} }
} }

View File

@ -19,6 +19,7 @@ import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context.NOTIFICATION_SERVICE import android.content.Context.NOTIFICATION_SERVICE
import android.content.pm.ServiceInfo
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
@ -62,7 +63,15 @@ abstract class PlayingNotification {
} }
if (newNotifyMode == NOTIFY_MODE_FOREGROUND) { if (newNotifyMode == NOTIFY_MODE_FOREGROUND) {
service.startForeground(NOTIFICATION_ID, notification) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
service.startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
)
} else {
service.startForeground(NOTIFICATION_ID,notification)
}
} else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) { } else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) {
notificationManager!!.notify(NOTIFICATION_ID, notification) notificationManager!!.notify(NOTIFICATION_ID, notification)
} }

View File

@ -25,6 +25,7 @@ import android.os.Build
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.media.app.NotificationCompat.MediaStyle import androidx.media.app.NotificationCompat.MediaStyle
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
@ -53,7 +54,7 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
stopped = false stopped = false
GlobalScope.launch { GlobalScope.launch {
val song = service.currentSong val song = service.currentSong
val playlist: PlaylistEntity? = MusicUtil.repository.favoritePlaylist() val playlist: PlaylistEntity = MusicUtil.repository.favoritePlaylist()
val isPlaying = service.isPlaying val isPlaying = service.isPlaying
val isFavorite = if (playlist != null) { val isFavorite = if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId) val songEntity = song.toSongEntity(playlist.playListId)
@ -69,11 +70,11 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel) action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val clickIntent = val clickIntent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PendingIntent.getActivity(
PendingIntent.getActivity(service, 0, action, PendingIntent.FLAG_IMMUTABLE) service, 0, action, if (VersionUtils.hasMarshmallow())
} else { PendingIntent.FLAG_IMMUTABLE
PendingIntent.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT) else 0 or PendingIntent.FLAG_UPDATE_CURRENT
} )
val serviceName = ComponentName(service, MusicService::class.java) val serviceName = ComponentName(service, MusicService::class.java)
val intent = Intent(ACTION_QUIT) val intent = Intent(ACTION_QUIT)
@ -82,7 +83,9 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
service, service,
0, 0,
intent, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0 or PendingIntent.FLAG_UPDATE_CURRENT
) )
val bigNotificationImageSize = service.resources val bigNotificationImageSize = service.resources
.getDimensionPixelSize(R.dimen.notification_big_image_size) .getDimensionPixelSize(R.dimen.notification_big_image_size)
@ -171,18 +174,16 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
.addAction(playPauseAction) .addAction(playPauseAction)
.addAction(nextAction) .addAction(nextAction)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder.setStyle(
builder.setStyle( MediaStyle()
MediaStyle() .setMediaSession(service.mediaSession.sessionToken)
.setMediaSession(service.mediaSession.sessionToken) .setShowActionsInCompactView(1, 2, 3)
.setShowActionsInCompactView(1, 2, 3) )
) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) if (Build.VERSION.SDK_INT <=
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification ) {
) { builder.color = color
builder.color = color
}
} }
if (stopped) { if (stopped) {
@ -199,6 +200,10 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
val serviceName = ComponentName(service, MusicService::class.java) val serviceName = ComponentName(service, MusicService::class.java)
val intent = Intent(action) val intent = Intent(action)
intent.component = serviceName intent.component = serviceName
return PendingIntent.getService(service, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getService(
service, 0, intent,
if (VersionUtils.hasMarshmallow()) PendingIntent.FLAG_IMMUTABLE
else 0 or PendingIntent.FLAG_UPDATE_CURRENT
)
} }
} }

View File

@ -83,7 +83,9 @@ class PlayingNotificationOreo : PlayingNotification() {
service, service,
0, 0,
action, action,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
) )
val deleteIntent = buildPendingIntent(service, ACTION_QUIT, null) val deleteIntent = buildPendingIntent(service, ACTION_QUIT, null)
@ -181,21 +183,21 @@ class PlayingNotificationOreo : PlayingNotification() {
service, service,
R.drawable.ic_close, R.drawable.ic_close,
primary primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER ), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
) )
val prev = createBitmap( val prev = createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, service,
R.drawable.ic_skip_previous_round_white_32dp, R.drawable.ic_skip_previous_round_white_32dp,
primary primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER ), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
) )
val next = createBitmap( val next = createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, service,
R.drawable.ic_skip_next_round_white_32dp, R.drawable.ic_skip_next_round_white_32dp,
primary primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER ), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
) )
val playPause = createBitmap( val playPause = createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
@ -204,7 +206,7 @@ class PlayingNotificationOreo : PlayingNotification() {
R.drawable.ic_pause_white_48dp R.drawable.ic_pause_white_48dp
else else
R.drawable.ic_play_arrow_white_48dp, primary R.drawable.ic_play_arrow_white_48dp, primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER ), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
) )
notificationLayout.setTextColor(R.id.title, primary) notificationLayout.setTextColor(R.id.title, primary)
@ -231,7 +233,7 @@ class PlayingNotificationOreo : PlayingNotification() {
service, service,
R.drawable.ic_notification, R.drawable.ic_notification,
secondary secondary
)!!, 0.6f ), 0.6f
) )
) )
notificationLayoutBig.setImageViewBitmap( notificationLayoutBig.setImageViewBitmap(
@ -241,7 +243,7 @@ class PlayingNotificationOreo : PlayingNotification() {
service, service,
R.drawable.ic_notification, R.drawable.ic_notification,
secondary secondary
)!!, 0.6f ), 0.6f
) )
) )
@ -262,7 +264,11 @@ class PlayingNotificationOreo : PlayingNotification() {
): PendingIntent { ): PendingIntent {
val intent = Intent(action) val intent = Intent(action)
intent.component = serviceName intent.component = serviceName
return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) return PendingIntent.getService(
context, 0, intent, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
} }

View File

@ -1,8 +0,0 @@
package code.name.monkey.retromusic.state
enum class NowPlayingPanelState {
EXPAND,
COLLAPSED_WITH,
COLLAPSED_WITHOUT,
HIDE,
}

View File

@ -16,26 +16,32 @@ package code.name.monkey.retromusic.transform
import android.view.View import android.view.View
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import kotlin.math.abs
class DepthTransformation : ViewPager.PageTransformer { class DepthTransformation : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) { override fun transformPage(page: View, position: Float) {
if (position < -1) { // [-Infinity,-1) when {
// This page is way off-screen to the left. position < -1 -> { // [-Infinity,-1)
page.alpha = 0f // This page is way off-screen to the left.
} else if (position <= 0) { // [-1,0] page.alpha = 0f
page.alpha = 1f }
page.translationX = 0f position <= 0 -> { // [-1,0]
page.scaleX = 1f page.alpha = 1f
page.scaleY = 1f page.translationX = 0f
} else if (position <= 1) { // (0,1] page.scaleX = 1f
page.translationX = -position * page.width page.scaleY = 1f
page.alpha = 1 - Math.abs(position) }
page.scaleX = 1 - Math.abs(position) position <= 1 -> { // (0,1]
page.scaleY = 1 - Math.abs(position) page.translationX = -position * page.width
} else { // (1,+Infinity] page.alpha = 1 - abs(position)
// This page is way off-screen to the right. page.scaleX = 1 - abs(position)
page.alpha = 0f page.scaleY = 1 - abs(position)
}
else -> { // (1,+Infinity]
// This page is way off-screen to the right.
page.alpha = 0f
}
} }
} }
} }

View File

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.transform
import android.view.View import android.view.View
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import kotlin.math.abs
class HorizontalFlipTransformation : ViewPager.PageTransformer { class HorizontalFlipTransformation : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) { override fun transformPage(page: View, position: Float) {
@ -30,21 +31,23 @@ class HorizontalFlipTransformation : ViewPager.PageTransformer {
} }
if (position < -1) { // [-Infinity,-1) when {
// This page is way off-screen to the left. position < -1 -> { // [-Infinity,-1)
page.alpha = 0f // This page is way off-screen to the left.
page.alpha = 0f
} else if (position <= 0) { // [-1,0] }
page.alpha = 1f position <= 0 -> { // [-1,0]
page.rotationX = 180 * (1 - Math.abs(position) + 1) page.alpha = 1f
page.rotationX = 180 * (1 - abs(position) + 1)
} else if (position <= 1) { // (0,1] }
page.alpha = 1f position <= 1 -> { // (0,1]
page.rotationX = -180 * (1 - Math.abs(position) + 1) page.alpha = 1f
page.rotationX = -180 * (1 - abs(position) + 1)
} else { // (1,+Infinity] }
// This page is way off-screen to the right. else -> { // (1,+Infinity]
page.alpha = 0f // This page is way off-screen to the right.
page.alpha = 0f
}
} }
} }
} }

View File

@ -16,6 +16,8 @@ package code.name.monkey.retromusic.transform
import android.view.View import android.view.View
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import kotlin.math.abs
import kotlin.math.max
/** /**
* @author Hemanth S (h4h13). * @author Hemanth S (h4h13).
@ -27,37 +29,41 @@ class NormalPageTransformer : ViewPager.PageTransformer {
val pageWidth = view.width val pageWidth = view.width
val pageHeight = view.height val pageHeight = view.height
if (position < -1) { // [-Infinity,-1) when {
// This page is way off-screen to the left. position < -1 -> { // [-Infinity,-1)
view.alpha = 1f // This page is way off-screen to the left.
view.scaleY = 0.7f view.alpha = 1f
} else if (position <= 1) { // [-1,1] view.scaleY = 0.7f
// Modify the default slide transition to shrink the page as well
val scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position))
val vertMargin = pageHeight * (1 - scaleFactor) / 2
val horzMargin = pageWidth * (1 - scaleFactor) / 2
if (position < 0) {
view.translationX = horzMargin - vertMargin / 2
} else {
view.translationX = -horzMargin + vertMargin / 2
} }
position <= 1 -> { // [-1,1]
// Modify the default slide transition to shrink the page as well
val scaleFactor = max(MIN_SCALE, 1 - abs(position))
val vertMargin = pageHeight * (1 - scaleFactor) / 2
val horzMargin = pageWidth * (1 - scaleFactor) / 2
if (position < 0) {
view.translationX = horzMargin - vertMargin / 2
} else {
view.translationX = -horzMargin + vertMargin / 2
}
// Scale the page down (between MIN_SCALE and 1) // Scale the page down (between MIN_SCALE and 1)
view.scaleX = scaleFactor view.scaleX = scaleFactor
view.scaleY = scaleFactor view.scaleY = scaleFactor
// Fade the page relative to its size. // Fade the page relative to its size.
//view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA)); //view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
} else { // (1,+Infinity] }
// This page is way off-screen to the right. else -> { // (1,+Infinity]
view.alpha = 1f // This page is way off-screen to the right.
view.scaleY = 0.7f view.alpha = 1f
view.scaleY = 0.7f
}
} }
} }
companion object { companion object {
private val MIN_SCALE = 0.85f private const val MIN_SCALE = 0.85f
private val MIN_ALPHA = 0.5f private const val MIN_ALPHA = 0.5f
} }
} }

View File

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.transform
import android.view.View import android.view.View
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import kotlin.math.abs
class VerticalFlipTransformation : ViewPager.PageTransformer { class VerticalFlipTransformation : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) { override fun transformPage(page: View, position: Float) {
@ -30,22 +31,24 @@ class VerticalFlipTransformation : ViewPager.PageTransformer {
} }
if (position < -1) { // [-Infinity,-1) when {
// This page is way off-screen to the left. position < -1 -> { // [-Infinity,-1)
page.alpha = 0f // This page is way off-screen to the left.
page.alpha = 0f
} else if (position <= 0) { // [-1,0] }
page.alpha = 1f position <= 0 -> { // [-1,0]
page.rotationY = 180 * (1 - Math.abs(position) + 1) page.alpha = 1f
page.rotationY = 180 * (1 - abs(position) + 1)
} else if (position <= 1) { // (0,1] }
page.alpha = 1f position <= 1 -> { // (0,1]
page.rotationY = -180 * (1 - Math.abs(position) + 1) page.alpha = 1f
page.rotationY = -180 * (1 - abs(position) + 1)
} else { // (1,+Infinity] }
// This page is way off-screen to the right. else -> { // (1,+Infinity]
page.alpha = 0f // This page is way off-screen to the right.
page.alpha = 0f
}
} }

View File

@ -79,26 +79,4 @@ object AppRater {
} }
} }
} }
private fun showRateDialog(context: Context, editor: SharedPreferences.Editor) {
MaterialAlertDialogBuilder(context)
.setTitle("Rate this App")
.setMessage("If you enjoy using Retro Music, please take a moment to rate it. Thanks for your support!")
.setPositiveButton(R.string.app_name) { _, _ ->
context.startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("market://details?id=${context.packageName}")
)
)
editor.putBoolean(DO_NOT_SHOW_AGAIN, true)
editor.commit()
}
.setNeutralButton("Not now", null)
.setNegativeButton("No thanks") { _, _ ->
editor.putBoolean(DO_NOT_SHOW_AGAIN, true)
editor.commit()
}
.show()
}
} }

View File

@ -3,11 +3,6 @@ package code.name.monkey.retromusic.util;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -20,19 +15,12 @@ import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.model.Song;
public class AutoGeneratedPlaylistBitmap { public class AutoGeneratedPlaylistBitmap {
private static final String TAG = "AutoGeneratedPB";
/*
public static Bitmap getBitmapWithCollectionFrame(Context context, List<Song> songPlaylist, boolean round, boolean blur) {
Bitmap bitmap = getBitmap(context,songPlaylist,round,blur);
int w = bitmap.getWidth();
Bitmap ret = Bitmap.createBitmap(w,w,Bitmap.Config.ARGB_8888);
}
*/
public static Bitmap getBitmap( public static Bitmap getBitmap(
Context context, List<Song> songPlaylist, boolean round, boolean blur) { Context context, List<Song> songPlaylist) {
if (songPlaylist == null || songPlaylist.isEmpty()) return getDefaultBitmap(context, round); if (songPlaylist == null || songPlaylist.isEmpty()) return getDefaultBitmap(context);
if (songPlaylist.size() == 1) return getBitmapWithAlbumId(context, songPlaylist.get(0).getAlbumId()); if (songPlaylist.size() == 1)
return getBitmapWithAlbumId(context, songPlaylist.get(0).getAlbumId());
List<Long> albumID = new ArrayList<>(); List<Long> albumID = new ArrayList<>();
for (Song song : songPlaylist) { for (Song song : songPlaylist) {
if (!albumID.contains(song.getAlbumId())) albumID.add(song.getAlbumId()); if (!albumID.contains(song.getAlbumId())) albumID.add(song.getAlbumId());
@ -40,108 +28,12 @@ public class AutoGeneratedPlaylistBitmap {
List<Bitmap> art = new ArrayList<>(); List<Bitmap> art = new ArrayList<>();
for (Long id : albumID) { for (Long id : albumID) {
Bitmap bitmap = getBitmapWithAlbumId(context, id); Bitmap bitmap = getBitmapWithAlbumId(context, id);
if (bitmap != null) art.add(bitmap); if (bitmap != null) art.add(BitmapEditor.getRoundedCornerBitmap(bitmap, 20));
if (art.size() == 9) break; if (art.size() == 9) break;
} }
return MergedImageUtils.INSTANCE.joinImages(art); return MergedImageUtils.INSTANCE.joinImages(art);
} }
private static Bitmap getBitmapCollection(ArrayList<Bitmap> art, boolean round) {
long start = System.currentTimeMillis();
// lấy kích thước là kích thước của bitmap lớn nhất
int max_width = art.get(0).getWidth();
for (Bitmap b : art) if (max_width < b.getWidth()) max_width = b.getWidth();
Bitmap bitmap = Bitmap.createBitmap(max_width, max_width, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(false);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(max_width / 100);
paint.setColor(0xffffffff);
switch (art.size()) {
case 2:
canvas.drawBitmap(art.get(1), null, new Rect(0, 0, max_width, max_width), null);
canvas.drawBitmap(
art.get(0), null, new Rect(-max_width / 2, 0, max_width / 2, max_width), null);
canvas.drawLine(max_width / 2, 0, max_width / 2, max_width, paint);
break;
case 3:
canvas.drawBitmap(
art.get(0), null, new Rect(-max_width / 4, 0, 3 * max_width / 4, max_width), null);
canvas.drawBitmap(
art.get(1), null, new Rect(max_width / 2, 0, max_width, max_width / 2), null);
canvas.drawBitmap(
art.get(2), null, new Rect(max_width / 2, max_width / 2, max_width, max_width), null);
canvas.drawLine(max_width / 2, 0, max_width / 2, max_width, paint);
canvas.drawLine(max_width / 2, max_width / 2, max_width, max_width / 2, paint);
break;
case 4:
canvas.drawBitmap(art.get(0), null, new Rect(0, 0, max_width / 2, max_width / 2), null);
canvas.drawBitmap(
art.get(1), null, new Rect(max_width / 2, 0, max_width, max_width / 2), null);
canvas.drawBitmap(
art.get(2), null, new Rect(0, max_width / 2, max_width / 2, max_width), null);
canvas.drawBitmap(
art.get(3), null, new Rect(max_width / 2, max_width / 2, max_width, max_width), null);
canvas.drawLine(max_width / 2, 0, max_width / 2, max_width, paint);
canvas.drawLine(0, max_width / 2, max_width, max_width / 2, paint);
break;
// default: canvas.drawBitmap(art.get(0),null,new Rect(0,0,max_width,max_width),null);
default:
// độ rộng của des bitmap
float w = (float) (Math.sqrt(2) / 2 * max_width);
float b = (float) (max_width / Math.sqrt(5));
// khoảng cách định nghĩa, dùng để tính vị trí tâm của 4 bức hình xung quanh
float d = (float) (max_width * (0.5f - 1 / Math.sqrt(10)));
float deg = 45;
for (int i = 0; i < 5; i++) {
canvas.save();
switch (i) {
case 0:
canvas.translate(max_width / 2, max_width / 2);
canvas.rotate(deg);
// b = (float) (max_width*Math.sqrt(2/5f));
canvas.drawBitmap(art.get(0), null, new RectF(-b / 2, -b / 2, b / 2, b / 2), null);
break;
case 1:
canvas.translate(d, 0);
canvas.rotate(deg);
canvas.drawBitmap(art.get(i), null, new RectF(-w / 2, -w / 2, w / 2, w / 2), null);
paint.setAntiAlias(true);
canvas.drawLine(w / 2, -w / 2, w / 2, w / 2, paint);
break;
case 2:
canvas.translate(max_width, d);
canvas.rotate(deg);
canvas.drawBitmap(art.get(i), null, new RectF(-w / 2, -w / 2, w / 2, w / 2), null);
paint.setAntiAlias(true);
canvas.drawLine(-w / 2, w / 2, w / 2, w / 2, paint);
break;
case 3:
canvas.translate(max_width - d, max_width);
canvas.rotate(deg);
canvas.drawBitmap(art.get(i), null, new RectF(-w / 2, -w / 2, w / 2, w / 2), null);
paint.setAntiAlias(true);
canvas.drawLine(-w / 2, -w / 2, -w / 2, w / 2, paint);
break;
case 4:
canvas.translate(0, max_width - d);
canvas.rotate(deg);
canvas.drawBitmap(art.get(i), null, new RectF(-w / 2, -w / 2, w / 2, w / 2), null);
paint.setAntiAlias(true);
canvas.drawLine(-w / 2, -w / 2, w / 2, -w / 2, paint);
break;
}
canvas.restore();
}
}
Log.d(TAG, "getBitmapCollection: smalltime = " + (System.currentTimeMillis() - start));
if (round) return BitmapEditor.getRoundedCornerBitmap(bitmap, bitmap.getWidth() / 40);
else return bitmap;
}
private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Long id) { private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Long id) {
try { try {
return Glide.with(context) return Glide.with(context)
@ -154,7 +46,7 @@ public class AutoGeneratedPlaylistBitmap {
} }
} }
public static Bitmap getDefaultBitmap(@NonNull Context context, boolean round) { public static Bitmap getDefaultBitmap(@NonNull Context context) {
return BitmapFactory.decodeResource(context.getResources(), R.drawable.default_album_art); return BitmapFactory.decodeResource(context.getResources(), R.drawable.default_album_art);
} }
} }

View File

@ -594,7 +594,7 @@ public final class BitmapEditor {
/** /**
* getResizedBitmap method is used to Resized the Image according to custom width and height * getResizedBitmap method is used to Resized the Image according to custom width and height
* *
* @param image * @param image image to be resized
* @param newHeight (new desired height) * @param newHeight (new desired height)
* @param newWidth (new desired Width) * @param newWidth (new desired Width)
* @return image (new resized image) * @return image (new resized image)
@ -609,22 +609,21 @@ public final class BitmapEditor {
// onTap the bit map // onTap the bit map
matrix.postScale(scaleWidth, scaleHeight); matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap // recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(image, 0, 0, width, height, matrix, false); return Bitmap.createBitmap(image, 0, 0, width, height, matrix, false);
return resizedBitmap;
} }
public static boolean TrueIfBitmapBigger(Bitmap bitmap, int size) { public static boolean TrueIfBitmapBigger(Bitmap bitmap, int size) {
int sizeBitmap = int sizeBitmap =
(bitmap.getHeight() > bitmap.getWidth()) ? bitmap.getHeight() : bitmap.getWidth(); Math.max(bitmap.getHeight(), bitmap.getWidth());
return sizeBitmap > size; return sizeBitmap > size;
} }
public static Bitmap GetRoundedBitmapWithBlurShadow( public static Bitmap getRoundedBitmapWithBlurShadow(
Bitmap original, int paddingTop, int paddingBottom, int paddingLeft, int paddingRight) { Bitmap original, int paddingTop, int paddingBottom, int paddingLeft, int paddingRight) {
int original_width = original.getWidth(); int original_width = original.getWidth();
int orginal_height = original.getHeight(); int original_height = original.getHeight();
int bitmap_width = original_width + paddingLeft + paddingRight; int bitmap_width = original_width + paddingLeft + paddingRight;
int bitmap_height = orginal_height + paddingTop + paddingBottom; int bitmap_height = original_height + paddingTop + paddingBottom;
Bitmap bitmap = Bitmap.createBitmap(bitmap_width, bitmap_height, Bitmap.Config.ARGB_8888); Bitmap bitmap = Bitmap.createBitmap(bitmap_width, bitmap_height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap); Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(); Paint paint = new Paint();

View File

@ -25,13 +25,11 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.media.ExifInterface; import android.media.ExifInterface;
import android.os.Build;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -102,10 +100,7 @@ public class ImageUtil {
public static Drawable getVectorDrawable( public static Drawable getVectorDrawable(
@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) { @NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) {
if (Build.VERSION.SDK_INT >= 21) { return res.getDrawable(resId, theme);
return res.getDrawable(resId, theme);
}
return VectorDrawableCompat.create(res, resId, theme);
} }
/** Makes sure that {@code mTempBuffer} has at least length {@code size}. */ /** Makes sure that {@code mTempBuffer} has at least length {@code size}. */

View File

@ -1,6 +1,9 @@
package code.name.monkey.retromusic.util package code.name.monkey.retromusic.util
import android.graphics.* import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import com.bumptech.glide.util.Util.assertBackgroundThread import com.bumptech.glide.util.Util.assertBackgroundThread
@ -75,25 +78,12 @@ internal object MergedImageUtils {
val bit = Bitmap.createScaledBitmap(bitmap, onePartSize, onePartSize, true) val bit = Bitmap.createScaledBitmap(bitmap, onePartSize, onePartSize, true)
canvas.drawBitmap( canvas.drawBitmap(
bit, bit,
(onePartSize * (i % parts)).toFloat(), (onePartSize * (i % parts)).toFloat() + (i % 3) * 50,
(onePartSize * (i / parts)).toFloat(), (onePartSize * (i / parts)).toFloat() + (i / 3) * 50,
paint paint
) )
bit.recycle() bit.recycle()
} }
paint.color = Color.WHITE
paint.strokeWidth = 10f
val oneThirdSize = (IMAGE_SIZE / 3).toFloat()
val twoThirdSize = (IMAGE_SIZE / 3 * 2).toFloat()
// vertical lines
canvas.drawLine(oneThirdSize, 0f, oneThirdSize, imageSize.toFloat(), paint)
canvas.drawLine(twoThirdSize, 0f, twoThirdSize, imageSize.toFloat(), paint)
// horizontal lines
canvas.drawLine(0f, oneThirdSize, imageSize.toFloat(), oneThirdSize, paint)
canvas.drawLine(0f, twoThirdSize, imageSize.toFloat(), twoThirdSize, paint)
return result return result
} }

View File

@ -348,7 +348,7 @@ object MusicUtil : KoinComponent {
val repository = get<Repository>() val repository = get<Repository>()
fun toggleFavorite(context: Context, song: Song) { fun toggleFavorite(context: Context, song: Song) {
GlobalScope.launch { GlobalScope.launch {
val playlist: PlaylistEntity? = repository.favoritePlaylist() val playlist: PlaylistEntity = repository.favoritePlaylist()
if (playlist != null) { if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId) val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty() val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
@ -529,7 +529,7 @@ object MusicUtil : KoinComponent {
val pendingIntent = MediaStore.createDeleteRequest(activity.contentResolver, songs.map { val pendingIntent = MediaStore.createDeleteRequest(activity.contentResolver, songs.map {
getSongFileUri(it.id) getSongFileUri(it.id)
}) })
activity.startIntentSenderForResult(pendingIntent.intentSender, 45, null, 0, 0, 0, null); activity.startIntentSenderForResult(pendingIntent.intentSender, 45, null, 0, 0, 0, null)
} }
fun songByGenre(genreId: Long): Song { fun songByGenre(genreId: Long): Song {

View File

@ -20,12 +20,10 @@ import android.content.Intent
import android.media.audiofx.AudioEffect import android.media.audiofx.AudioEffect
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.navigation.findNavController
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.* import code.name.monkey.retromusic.activities.*
import code.name.monkey.retromusic.activities.bugreport.BugReportActivity import code.name.monkey.retromusic.activities.bugreport.BugReportActivity
import code.name.monkey.retromusic.helper.MusicPlayerRemote.audioSessionId import code.name.monkey.retromusic.helper.MusicPlayerRemote.audioSessionId
import com.google.android.material.bottomsheet.BottomSheetBehavior
object NavigationUtil { object NavigationUtil {
fun bugReport(activity: Activity) { fun bugReport(activity: Activity) {
@ -40,21 +38,6 @@ object NavigationUtil {
ActivityCompat.startActivity(activity, Intent(activity, LicenseActivity::class.java), null) ActivityCompat.startActivity(activity, Intent(activity, LicenseActivity::class.java), null)
} }
fun goToLyrics(activity: Activity) {
if (activity !is MainActivity) return
activity.apply {
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
setBottomNavVisibility(false)
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
collapsePanel()
}
findNavController(R.id.fragment_container).navigate(
R.id.lyrics_fragment
)
}
}
fun goToProVersion(context: Context) { fun goToProVersion(context: Context) {
ActivityCompat.startActivity(context, Intent(context, PurchaseActivity::class.java), null) ActivityCompat.startActivity(context, Intent(context, PurchaseActivity::class.java), null)
} }

View File

@ -191,6 +191,7 @@ class PackageValidator(
* *
* @return [PackageInfo] for the package name or null if it's not found. * @return [PackageInfo] for the package name or null if it's not found.
*/ */
@Suppress("Deprecation")
@SuppressLint("PackageManagerGetSignatures") @SuppressLint("PackageManagerGetSignatures")
private fun getPackageInfo(callingPackage: String): PackageInfo? = private fun getPackageInfo(callingPackage: String): PackageInfo? =
packageManager.getPackageInfo(callingPackage, packageManager.getPackageInfo(callingPackage,
@ -208,11 +209,11 @@ class PackageValidator(
private fun getSignature(packageInfo: PackageInfo): String? { private fun getSignature(packageInfo: PackageInfo): String? {
// Security best practices dictate that an app should be signed with exactly one (1) // Security best practices dictate that an app should be signed with exactly one (1)
// signature. Because of this, if there are multiple signatures, reject it. // signature. Because of this, if there are multiple signatures, reject it.
if (packageInfo.signatures == null || packageInfo.signatures.size != 1) { return if (packageInfo.signatures == null || packageInfo.signatures.size != 1) {
return null null
} else { } else {
val certificate = packageInfo.signatures[0].toByteArray() val certificate = packageInfo.signatures[0].toByteArray()
return getSignatureSha256(certificate) getSignatureSha256(certificate)
} }
} }

View File

@ -36,7 +36,6 @@ import java.util.List;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.db.PlaylistWithSongs; import code.name.monkey.retromusic.db.PlaylistWithSongs;
import code.name.monkey.retromusic.helper.M3UConstants;
import code.name.monkey.retromusic.helper.M3UWriter; import code.name.monkey.retromusic.helper.M3UWriter;
import code.name.monkey.retromusic.model.Playlist; import code.name.monkey.retromusic.model.Playlist;
import code.name.monkey.retromusic.model.PlaylistSong; import code.name.monkey.retromusic.model.PlaylistSong;
@ -167,8 +166,8 @@ public class PlaylistsUtil {
Toast.LENGTH_SHORT) Toast.LENGTH_SHORT)
.show(); .show();
} }
} catch (SecurityException ignored) { } catch (SecurityException exception) {
ignored.printStackTrace(); exception.printStackTrace();
} }
} }
@ -235,13 +234,13 @@ public class PlaylistsUtil {
for (int i = 0; i < selectionArgs.length; i++) { for (int i = 0; i < selectionArgs.length; i++) {
selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList()); selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList());
} }
String selection = MediaStore.Audio.Playlists.Members._ID + " in ("; StringBuilder selection = new StringBuilder(MediaStore.Audio.Playlists.Members._ID + " in (");
//noinspection unused
for (String selectionArg : selectionArgs) selection += "?, "; for (String selectionArg : selectionArgs) selection.append("?, ");
selection = selection.substring(0, selection.length() - 2) + ")"; selection = new StringBuilder(selection.substring(0, selection.length() - 2) + ")");
try { try {
context.getContentResolver().delete(uri, selection, selectionArgs); context.getContentResolver().delete(uri, selection.toString(), selectionArgs);
} catch (SecurityException ignored) { } catch (SecurityException ignored) {
} }
} }

View File

@ -12,6 +12,7 @@ import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.extensions.getIntRes import code.name.monkey.retromusic.extensions.getIntRes
import code.name.monkey.retromusic.extensions.getStringOrDefault import code.name.monkey.retromusic.extensions.getStringOrDefault
import code.name.monkey.retromusic.fragments.AlbumCoverStyle import code.name.monkey.retromusic.fragments.AlbumCoverStyle
import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.NowPlayingScreen import code.name.monkey.retromusic.fragments.NowPlayingScreen
import code.name.monkey.retromusic.fragments.folder.FoldersFragment import code.name.monkey.retromusic.fragments.folder.FoldersFragment
import code.name.monkey.retromusic.helper.SortOrder.* import code.name.monkey.retromusic.helper.SortOrder.*
@ -344,22 +345,40 @@ object PreferenceUtil {
putInt(LYRICS_OPTIONS, value) putInt(LYRICS_OPTIONS, value)
} }
var songGridStyle var songGridStyle: GridStyle
get() = sharedPreferences.getInt(SONG_GRID_STYLE, R.layout.item_grid) get() {
val id: Int = sharedPreferences.getInt(SONG_GRID_STYLE, 0)
// We can directly use "first" kotlin extension function here but
// there maybe layout id stored in this so to avoid a crash we use
// "firstOrNull"
return GridStyle.values().firstOrNull { gridStyle ->
gridStyle.id == id
} ?: GridStyle.Grid
}
set(value) = sharedPreferences.edit { set(value) = sharedPreferences.edit {
putInt(SONG_GRID_STYLE, value) putInt(SONG_GRID_STYLE, value.id)
} }
var albumGridStyle var albumGridStyle: GridStyle
get() = sharedPreferences.getInt(ALBUM_GRID_STYLE, R.layout.item_grid) get() {
val id: Int = sharedPreferences.getInt(ALBUM_GRID_STYLE, 0)
return GridStyle.values().firstOrNull { gridStyle ->
gridStyle.id == id
} ?: GridStyle.Grid
}
set(value) = sharedPreferences.edit { set(value) = sharedPreferences.edit {
putInt(ALBUM_GRID_STYLE, value) putInt(ALBUM_GRID_STYLE, value.id)
} }
var artistGridStyle var artistGridStyle: GridStyle
get() = sharedPreferences.getInt(ARTIST_GRID_STYLE, R.layout.item_grid_circle) get() {
val id: Int = sharedPreferences.getInt(ARTIST_GRID_STYLE, 4)
return GridStyle.values().firstOrNull { gridStyle ->
gridStyle.id == id
} ?: GridStyle.Circular
}
set(value) = sharedPreferences.edit { set(value) = sharedPreferences.edit {
putInt(ARTIST_GRID_STYLE, value) putInt(ARTIST_GRID_STYLE, value.id)
} }
val filterLength get() = sharedPreferences.getInt(FILTER_SONG, 20) val filterLength get() = sharedPreferences.getInt(FILTER_SONG, 20)
@ -498,6 +517,25 @@ object PreferenceUtil {
} }
var playlistGridSize
get() = sharedPreferences.getInt(
PLAYLIST_GRID_SIZE,
App.getContext().getIntRes(R.integer.default_grid_columns)
)
set(value) = sharedPreferences.edit {
putInt(PLAYLIST_GRID_SIZE, value)
}
var playlistGridSizeLand
get() = sharedPreferences.getInt(
PLAYLIST_GRID_SIZE_LAND,
App.getContext().getIntRes(R.integer.default_grid_columns_land)
)
set(value) = sharedPreferences.edit {
putInt(PLAYLIST_GRID_SIZE, value)
}
var albumCoverStyle: AlbumCoverStyle var albumCoverStyle: AlbumCoverStyle
get() { get() {
val id: Int = sharedPreferences.getInt(ALBUM_COVER_STYLE, 0) val id: Int = sharedPreferences.getInt(ALBUM_COVER_STYLE, 0)

View File

@ -42,7 +42,7 @@ public class RetroColorUtil {
float[] hsv = new float[3]; float[] hsv = new float[3];
Color.colorToHSV(color, hsv); Color.colorToHSV(color, hsv);
hsv[1] = (hsv[1] / 1 * ratio) + (0.2f * (1.0f - ratio)); hsv[1] = (hsv[1] * ratio) + (0.2f * (1.0f - ratio));
return Color.HSVToColor(hsv); return Color.HSVToColor(hsv);
} }
@ -188,7 +188,7 @@ public class RetroColorUtil {
public static int getDominantColor(Bitmap bitmap, int defaultFooterColor) { public static int getDominantColor(Bitmap bitmap, int defaultFooterColor) {
List<Palette.Swatch> swatchesTemp = Palette.from(bitmap).generate().getSwatches(); List<Palette.Swatch> swatchesTemp = Palette.from(bitmap).generate().getSwatches();
List<Palette.Swatch> swatches = new ArrayList<Palette.Swatch>(swatchesTemp); List<Palette.Swatch> swatches = new ArrayList<>(swatchesTemp);
Collections.sort( Collections.sort(
swatches, (swatch1, swatch2) -> swatch2.getPopulation() - swatch1.getPopulation()); swatches, (swatch1, swatch2) -> swatch2.getPopulation() - swatch1.getPopulation());
return swatches.size() > 0 ? swatches.get(0).getRgb() : defaultFooterColor; return swatches.size() > 0 ? swatches.get(0).getRgb() : defaultFooterColor;

View File

@ -40,7 +40,7 @@ import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import androidx.core.content.res.ResourcesCompat;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
@ -127,14 +127,14 @@ public class RetroUtil {
return result; return result;
} }
@Nullable @NonNull
public static Drawable getTintedVectorDrawable( public static Drawable getTintedVectorDrawable(
@NonNull Context context, @DrawableRes int id, @ColorInt int color) { @NonNull Context context, @DrawableRes int id, @ColorInt int color) {
return TintHelper.createTintedDrawable( return TintHelper.createTintedDrawable(
getVectorDrawable(context.getResources(), id, context.getTheme()), color); getVectorDrawable(context.getResources(), id, context.getTheme()), color);
} }
@Nullable @NonNull
public static Drawable getTintedVectorDrawable( public static Drawable getTintedVectorDrawable(
@NonNull Resources res, @NonNull Resources res,
@DrawableRes int resId, @DrawableRes int resId,
@ -146,10 +146,7 @@ public class RetroUtil {
@Nullable @Nullable
public static Drawable getVectorDrawable( public static Drawable getVectorDrawable(
@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) { @NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) {
if (Build.VERSION.SDK_INT >= 21) { return ResourcesCompat.getDrawable(res, resId, theme);
return res.getDrawable(resId, theme);
}
return VectorDrawableCompat.create(res, resId, theme);
} }
public static void hideSoftKeyboard(@Nullable Activity activity) { public static void hideSoftKeyboard(@Nullable Activity activity) {

View File

@ -1,147 +0,0 @@
package code.name.monkey.retromusic.util;
import android.annotation.TargetApi;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Build;
import android.util.StateSet;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
public class RippleUtils {
public static final boolean USE_FRAMEWORK_RIPPLE =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
private static final int[] PRESSED_STATE_SET = {
android.R.attr.state_pressed,
};
private static final int[] HOVERED_FOCUSED_STATE_SET = {
android.R.attr.state_hovered, android.R.attr.state_focused,
};
private static final int[] FOCUSED_STATE_SET = {
android.R.attr.state_focused,
};
private static final int[] HOVERED_STATE_SET = {
android.R.attr.state_hovered,
};
private static final int[] SELECTED_PRESSED_STATE_SET = {
android.R.attr.state_selected, android.R.attr.state_pressed,
};
private static final int[] SELECTED_HOVERED_FOCUSED_STATE_SET = {
android.R.attr.state_selected, android.R.attr.state_hovered, android.R.attr.state_focused,
};
private static final int[] SELECTED_FOCUSED_STATE_SET = {
android.R.attr.state_selected, android.R.attr.state_focused,
};
private static final int[] SELECTED_HOVERED_STATE_SET = {
android.R.attr.state_selected, android.R.attr.state_hovered,
};
private static final int[] SELECTED_STATE_SET = {
android.R.attr.state_selected,
};
private static final int[] ENABLED_PRESSED_STATE_SET = {
android.R.attr.state_enabled, android.R.attr.state_pressed
};
public static ColorStateList convertToRippleDrawableColor(@Nullable ColorStateList rippleColor) {
if (USE_FRAMEWORK_RIPPLE) {
int size = 2;
final int[][] states = new int[size][];
final int[] colors = new int[size];
int i = 0;
// Ideally we would define a different composite color for each state, but that causes the
// ripple animation to abort prematurely.
// So we only allow two base states: selected, and non-selected. For each base state, we only
// base the ripple composite on its pressed state.
// Selected base state.
states[i] = SELECTED_STATE_SET;
colors[i] = getColorForState(rippleColor, SELECTED_PRESSED_STATE_SET);
i++;
// Non-selected base state.
states[i] = StateSet.NOTHING;
colors[i] = getColorForState(rippleColor, PRESSED_STATE_SET);
i++;
return new ColorStateList(states, colors);
} else {
int size = 10;
final int[][] states = new int[size][];
final int[] colors = new int[size];
int i = 0;
states[i] = SELECTED_PRESSED_STATE_SET;
colors[i] = getColorForState(rippleColor, SELECTED_PRESSED_STATE_SET);
i++;
states[i] = SELECTED_HOVERED_FOCUSED_STATE_SET;
colors[i] = getColorForState(rippleColor, SELECTED_HOVERED_FOCUSED_STATE_SET);
i++;
states[i] = SELECTED_FOCUSED_STATE_SET;
colors[i] = getColorForState(rippleColor, SELECTED_FOCUSED_STATE_SET);
i++;
states[i] = SELECTED_HOVERED_STATE_SET;
colors[i] = getColorForState(rippleColor, SELECTED_HOVERED_STATE_SET);
i++;
// Checked state.
states[i] = SELECTED_STATE_SET;
colors[i] = Color.TRANSPARENT;
i++;
states[i] = PRESSED_STATE_SET;
colors[i] = getColorForState(rippleColor, PRESSED_STATE_SET);
i++;
states[i] = HOVERED_FOCUSED_STATE_SET;
colors[i] = getColorForState(rippleColor, HOVERED_FOCUSED_STATE_SET);
i++;
states[i] = FOCUSED_STATE_SET;
colors[i] = getColorForState(rippleColor, FOCUSED_STATE_SET);
i++;
states[i] = HOVERED_STATE_SET;
colors[i] = getColorForState(rippleColor, HOVERED_STATE_SET);
i++;
// Default state.
states[i] = StateSet.NOTHING;
colors[i] = Color.TRANSPARENT;
i++;
return new ColorStateList(states, colors);
}
}
@ColorInt
private static int getColorForState(@Nullable ColorStateList rippleColor, int[] state) {
int color;
if (rippleColor != null) {
color = rippleColor.getColorForState(state, rippleColor.getDefaultColor());
} else {
color = Color.TRANSPARENT;
}
return USE_FRAMEWORK_RIPPLE ? doubleAlpha(color) : color;
}
/**
* On API 21+, the framework composites a ripple color onto the display at about 50% opacity.
* Since we are providing precise ripple colors, cancel that out by doubling the opacity here.
*/
@ColorInt
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static int doubleAlpha(@ColorInt int color) {
int alpha = Math.min(2 * Color.alpha(color), 255);
return ColorUtils.setAlphaComponent(color, alpha);
}
}

View File

@ -54,7 +54,7 @@ public class SAFUtil {
public static final int REQUEST_SAF_PICK_TREE = 43; public static final int REQUEST_SAF_PICK_TREE = 43;
public static boolean isSAFRequired(File file) { public static boolean isSAFRequired(File file) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !file.canWrite(); return !file.canWrite();
} }
public static boolean isSAFRequired(String path) { public static boolean isSAFRequired(String path) {

View File

@ -29,22 +29,22 @@ public class SwipeAndDragHelper extends ItemTouchHelper.Callback {
} }
@Override @Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return makeMovementFlags(dragFlags, 0); return makeMovementFlags(dragFlags, 0);
} }
@Override @Override
public boolean onMove( public boolean onMove(
RecyclerView recyclerView, @NonNull RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) { RecyclerView.ViewHolder target) {
contract.onViewMoved(viewHolder.getLayoutPosition(), target.getLayoutPosition()); contract.onViewMoved(viewHolder.getLayoutPosition(), target.getLayoutPosition());
return true; return true;
} }
@Override @Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {} public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {}
@Override @Override
public boolean isLongPressDragEnabled() { public boolean isLongPressDragEnabled() {
@ -53,13 +53,13 @@ public class SwipeAndDragHelper extends ItemTouchHelper.Callback {
@Override @Override
public void onChildDraw( public void onChildDraw(
Canvas c, @NonNull Canvas c,
RecyclerView recyclerView, @NonNull RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder,
float dX, float dX,
float dY, float dY,
int actionState, int actionState,
boolean isCurrentlyActive) { boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
float alpha = 1 - (Math.abs(dX) / recyclerView.getWidth()); float alpha = 1 - (Math.abs(dX) / recyclerView.getWidth());
viewHolder.itemView.setAlpha(alpha); viewHolder.itemView.setAlpha(alpha);

View File

@ -76,11 +76,6 @@ object ViewUtil {
) )
} }
fun setProgressDrawable(indicator: CircularProgressIndicator, newColor: Int) {
indicator.setIndicatorColor(newColor)
indicator.trackColor = ColorUtil.withAlpha(newColor, 0.2f)
}
fun hitTest(v: View, x: Int, y: Int): Boolean { fun hitTest(v: View, x: Int, y: Int): Boolean {
val tx = (v.translationX + 0.5f).toInt() val tx = (v.translationX + 0.5f).toInt()
val ty = (v.translationY + 0.5f).toInt() val ty = (v.translationY + 0.5f).toInt()

View File

@ -75,11 +75,9 @@ public class MediaNotificationProcessor {
*/ */
private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10; private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
private static final String TAG = "ColorPicking";
private float[] mFilteredBackgroundHsl = null; private float[] mFilteredBackgroundHsl = null;
private final Palette.Filter mBlackWhiteFilter = private final Palette.Filter mBlackWhiteFilter =
(rgb, hsl) -> !isWhiteOrBlack(hsl); (rgb, hsl) -> !isWhiteOrBlack(hsl);
private boolean mIsLowPriority;
private int backgroundColor; private int backgroundColor;
private int secondaryTextColor; private int secondaryTextColor;
private int primaryTextColor; private int primaryTextColor;
@ -376,10 +374,6 @@ public class MediaNotificationProcessor {
return hslColor[2] >= WHITE_MIN_LIGHTNESS; return hslColor[2] >= WHITE_MIN_LIGHTNESS;
} }
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
}
private void ensureColors(int backgroundColor, int mForegroundColor) { private void ensureColors(int backgroundColor, int mForegroundColor) {
{ {
double backLum = NotificationColorUtil.calculateLuminance(backgroundColor); double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);

View File

@ -55,7 +55,7 @@ public class NotificationColorUtil {
private final ImageUtils mImageUtils = new ImageUtils(); private final ImageUtils mImageUtils = new ImageUtils();
private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache = private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
new WeakHashMap<Bitmap, Pair<Boolean, Integer>>(); new WeakHashMap<>();
private final int mGrayscaleIconMaxSize; // @dimen/notification_large_icon_width (64dp) private final int mGrayscaleIconMaxSize; // @dimen/notification_large_icon_width (64dp)
@ -353,11 +353,7 @@ public class NotificationColorUtil {
public static int resolvePrimaryColor(Context context, int backgroundColor) { public static int resolvePrimaryColor(Context context, int backgroundColor) {
boolean useDark = shouldUseDark(backgroundColor); boolean useDark = shouldUseDark(backgroundColor);
if (useDark) { return ContextCompat.getColor(context, android.R.color.primary_text_light);
return ContextCompat.getColor(context, android.R.color.primary_text_light);
} else {
return ContextCompat.getColor(context, android.R.color.primary_text_light);
}
} }
public static int resolveSecondaryColor(Context context, int backgroundColor) { public static int resolveSecondaryColor(Context context, int backgroundColor) {
@ -831,11 +827,11 @@ public class NotificationColorUtil {
} }
private static int constrain(int amount, int low, int high) { private static int constrain(int amount, int low, int high) {
return amount < low ? low : (amount > high ? high : amount); return amount < low ? low : (Math.min(amount, high));
} }
private static float constrain(float amount, float low, float high) { private static float constrain(float amount, float low, float high) {
return amount < low ? low : (amount > high ? high : amount); return amount < low ? low : (Math.min(amount, high));
} }
private static double pivotXyzComponent(double component) { private static double pivotXyzComponent(double component) {

View File

@ -399,6 +399,7 @@ public class BreadCrumbLayout extends HorizontalScrollView implements View.OnCli
return file.getPath().equals("/") ? "root" : file.getName(); return file.getPath().equals("/") ? "root" : file.getName();
} }
@NonNull
@Override @Override
public String toString() { public String toString() {
return "Crumb{" + "file=" + file + ", scrollPos=" + scrollPos + '}'; return "Crumb{" + "file=" + file + ", scrollPos=" + scrollPos + '}';

View File

@ -209,10 +209,8 @@ public class CircularImageView extends AppCompatImageView {
private void drawShadow(float shadowRadius, int shadowColor) { private void drawShadow(float shadowRadius, int shadowColor) {
this.shadowRadius = shadowRadius; this.shadowRadius = shadowRadius;
this.shadowColor = shadowColor; this.shadowColor = shadowColor;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
setLayerType(LAYER_TYPE_SOFTWARE, paintBorder); setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
} paintBorder.setShadowLayer(shadowRadius, 0.0f, shadowRadius / 2, shadowColor);
paintBorder.setShadowLayer(shadowRadius, 0.0f, shadowRadius / 2, shadowColor);
} }
private void updateShader() { private void updateShader() {

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