Merge pull request #1176 from prathameshmm02/dev

Bug Fixes & Improvements
This commit is contained in:
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 {
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 31
renderscriptTargetApi 29//must match target sdk and build tools
vectorDrawables.useSupportLibrary = true
@ -126,7 +126,7 @@ dependencies {
def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$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"
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-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-android:$koin_version"
@ -157,7 +157,7 @@ dependencies {
implementation 'com.anjlab.android.iab.v3:library:2.0.3'
implementation 'com.r0adkll:slidableactivity:2.1.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 'cat.ereza:customactivityoncrash:2.3.0'
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'

View file

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

View file

@ -37,6 +37,7 @@ object Constants {
const val IS_MUSIC =
MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"
@Suppress("Deprecation")
val baseProjection = arrayOf(
BaseColumns._ID, // 0
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 ARTIST_GRID_SIZE = "artist_grid_size"
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 AUDIO_DUCKING = "audio_ducking"
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.setDefault(localeList);
configuration.setLocales(localeList);
context = context.createConfigurationContext(configuration);
} else if (VersionUtils.INSTANCE.hasLollipop()) {
configuration.setLocale(newLocale);
context = context.createConfigurationContext(configuration);
} else {
configuration.locale = newLocale;
res.updateConfiguration(configuration, res.getDisplayMetrics());
configuration.setLocale(newLocale);
}
context = context.createConfigurationContext(configuration);
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() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)

View file

@ -132,7 +132,7 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
permissionDeniedMessage!!,
Snackbar.LENGTH_INDEFINITE
)
.setAction(code.name.monkey.retromusic.R.string.action_grant) { requestPermissions() }
.setAction(R.string.action_grant) { requestPermissions() }
.setActionTextColor(ThemeStore.accentColor(this)).show()
} else {
// 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,
permissionDeniedMessage!!,
Snackbar.LENGTH_INDEFINITE
).setAction(code.name.monkey.retromusic.R.string.action_settings) {
).setAction(R.string.action_settings) {
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
@ -172,7 +172,7 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
v.getGlobalVisibleRect(outRect)
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
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.appcompat.app.AppCompatDelegate.setDefaultNightMode
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.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
@ -118,12 +120,11 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
if (statusBar != null) {
when {
VersionUtils.hasMarshmallow() -> statusBar.setBackgroundColor(color)
VersionUtils.hasLollipop() -> statusBar.setBackgroundColor(
else -> statusBar.setBackgroundColor(
ColorUtil.darkenColor(
color
)
)
else -> statusBar.setBackgroundColor(color)
}
} else {
when {
@ -193,16 +194,20 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
}
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) {
window.decorView.systemUiVisibility = flags
WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
hide(WindowInsetsCompat.Type.systemBars())
}
}
}
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() {

View file

@ -7,6 +7,7 @@ import android.content.pm.PackageManager;
import android.os.Build;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import java.util.Arrays;
import java.util.Locale;
@ -16,19 +17,16 @@ import code.name.monkey.retromusic.util.PreferenceUtil;
public class DeviceInfo {
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private final String[] abis =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
? Build.SUPPORTED_ABIS
: new String[] {Build.CPU_ABI, Build.CPU_ABI2};
Build.SUPPORTED_ABIS;
@SuppressLint("NewApi")
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")
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;
@ -138,6 +136,7 @@ public class DeviceInfo {
+ "</table>\n";
}
@NonNull
@Override
public String toString() {
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) {
try {
kotlin.runCatching {
var artwork: Artwork? = null
var albumArtFile: File? = null
if (info.artworkInfo?.artwork != null) {
@ -113,18 +113,18 @@ class TagWriter {
deleteAlbumArt(context, info.artworkInfo!!.albumId)
}
scan(context, info.filePaths)
} catch (e: Exception) {
e.printStackTrace()
scan(context, null)
}.onFailure {
it.printStackTrace()
}
}
}
@RequiresApi(Build.VERSION_CODES.R)
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo) =
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
withContext(Dispatchers.IO) {
val cacheFiles = mutableListOf<File>()
try {
kotlin.runCatching {
var artwork: Artwork? = null
var albumArtFile: File? = null
if (info.artworkInfo?.artwork != null) {
@ -193,11 +193,10 @@ class TagWriter {
} else if (deletedArtwork) {
deleteAlbumArt(context, info.artworkInfo!!.albumId)
}
cacheFiles
} catch (e: Exception) {
e.printStackTrace()
listOf()
}.onFailure {
it.printStackTrace()
}
cacheFiles
}
}
}

View file

@ -14,6 +14,7 @@
*/
package code.name.monkey.retromusic.adapter
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -51,6 +52,8 @@ class HomeAdapter(
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IArtistClickListener, IAlbumClickListener,
IGenreClickListener {
private var mLastClickTime: Long = 0
private var list = listOf<Home>()
override fun getItemViewType(position: Int): Int {
@ -194,6 +197,10 @@ class HomeAdapter(
itemView.findViewById<TextView>(R.id.message).apply {
setTextColor(color)
setOnClickListener {
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000){
return@setOnClickListener
}
mLastClickTime = SystemClock.elapsedRealtime()
MusicPlayerRemote.playNext((home.arrayList as List<Song>).subList(0, 8))
if (!MusicPlayerRemote.isPlaying) {
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 {
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.fragments.AlbumCoverStyle
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.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
import code.name.monkey.retromusic.model.Song
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.color.MediaNotificationProcessor
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
@ -127,7 +127,8 @@ class AlbumCoverPagerAdapter(
setTitle(song.title)
setMessage(if (data.isNullOrEmpty()) "No lyrics found" else data)
setNegativeButton(R.string.synced_lyrics) { _, _ ->
NavigationUtil.goToLyrics(requireActivity())
goToLyrics(requireActivity())
}
show()
}

View file

@ -22,6 +22,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.ViewCompat
import androidx.core.view.setPadding
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
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.PlaylistWithSongs
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.show
import code.name.monkey.retromusic.glide.GlideApp
@ -42,7 +44,7 @@ import code.name.monkey.retromusic.util.MusicUtil
class PlaylistAdapter(
override val activity: FragmentActivity,
private var dataSet: List<PlaylistWithSongs>,
var dataSet: List<PlaylistWithSongs>,
private var itemLayoutRes: Int,
ICabHolder: ICabHolder?,
private val listener: IPlaylistClickListener
@ -94,7 +96,12 @@ class PlaylistAdapter(
holder.menu?.show()
}
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()
.into(holder.image!!)
}

View file

@ -188,7 +188,7 @@ class PlayingQueueAdapter(
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) {
SwipeResultActionDefault()
} 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 {
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 {
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.widget.RemoteViews
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.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
@ -61,7 +62,7 @@ class AppWidgetBig : BaseAppWidget() {
context,
R.drawable.ic_skip_next,
MaterialValueHelper.getPrimaryTextColor(context, false)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -70,16 +71,16 @@ class AppWidgetBig : BaseAppWidget() {
context,
R.drawable.ic_skip_previous,
MaterialValueHelper.getPrimaryTextColor(context, false)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, BaseAppWidget.createBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_play_arrow_white_32dp,
MaterialValueHelper.getPrimaryTextColor(context, false)
)!!, 1f
), 1f
)
)
@ -126,7 +127,7 @@ class AppWidgetBig : BaseAppWidget() {
service,
playPauseRes,
primaryColor
)!!, 1f
), 1f
)
)
@ -137,7 +138,7 @@ class AppWidgetBig : BaseAppWidget() {
service,
R.drawable.ic_skip_next,
primaryColor
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -146,7 +147,7 @@ class AppWidgetBig : BaseAppWidget() {
service,
R.drawable.ic_skip_previous,
primaryColor
)!!, 1f
), 1f
)
)
@ -155,7 +156,7 @@ class AppWidgetBig : BaseAppWidget() {
// Load the album cover async and push the update on completion
val p = RetroUtil.getScreenSize(service)
val widgetImageSize = Math.min(p.x, p.y)
val widgetImageSize = p.x.coerceAtMost(p.y)
val appContext = service.applicationContext
service.runOnUiThread {
if (target != null) {
@ -208,7 +209,11 @@ class AppWidgetBig : BaseAppWidget() {
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
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)
// Previous track

View file

@ -24,6 +24,7 @@ import android.text.TextUtils
import android.view.View
import android.widget.RemoteViews
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.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
@ -59,7 +60,7 @@ class AppWidgetCard : BaseAppWidget() {
context,
R.drawable.ic_skip_next,
secondaryColor
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -68,7 +69,7 @@ class AppWidgetCard : BaseAppWidget() {
context,
R.drawable.ic_skip_previous,
secondaryColor
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -77,7 +78,7 @@ class AppWidgetCard : BaseAppWidget() {
context,
R.drawable.ic_play_arrow_white_32dp,
secondaryColor
)!!, 1f
), 1f
)
)
@ -112,7 +113,7 @@ class AppWidgetCard : BaseAppWidget() {
service,
playPauseRes,
MaterialValueHelper.getSecondaryTextColor(service, true)
)!!, 1f
), 1f
)
)
@ -123,7 +124,7 @@ class AppWidgetCard : BaseAppWidget() {
service,
R.drawable.ic_skip_next,
MaterialValueHelper.getSecondaryTextColor(service, true)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -132,7 +133,7 @@ class AppWidgetCard : BaseAppWidget() {
service,
R.drawable.ic_skip_previous,
MaterialValueHelper.getSecondaryTextColor(service, true)
)!!, 1f
), 1f
)
)
@ -231,7 +232,11 @@ class AppWidgetCard : BaseAppWidget() {
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
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.media_titles, pendingIntent)

View file

@ -25,6 +25,7 @@ import android.text.TextUtils
import android.view.View
import android.widget.RemoteViews
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.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
@ -60,7 +61,7 @@ class AppWidgetClassic : BaseAppWidget() {
context,
R.drawable.ic_skip_next,
MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -70,7 +71,7 @@ class AppWidgetClassic : BaseAppWidget() {
context,
R.drawable.ic_skip_previous,
MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -80,7 +81,7 @@ class AppWidgetClassic : BaseAppWidget() {
context,
R.drawable.ic_play_arrow_white_32dp,
MaterialValueHelper.getSecondaryTextColor(context, true)
)!!, 1f
), 1f
)
)
@ -221,7 +222,11 @@ class AppWidgetClassic : BaseAppWidget() {
// Home
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.media_titles, pendingIntent)

View file

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

View file

@ -18,11 +18,11 @@ import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.provider.MediaStore
import android.text.TextUtils
import android.view.View
import android.widget.RemoteViews
import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity
@ -42,7 +42,7 @@ class AppWidgetText : BaseAppWidget() {
context, R.drawable.ic_skip_next, ContextCompat.getColor(
context, R.color.md_white_1000
)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -51,7 +51,7 @@ class AppWidgetText : BaseAppWidget() {
context, R.drawable.ic_skip_previous, ContextCompat.getColor(
context, R.color.md_white_1000
)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -60,7 +60,7 @@ class AppWidgetText : BaseAppWidget() {
context, R.drawable.ic_play_arrow_white_32dp, ContextCompat.getColor(
context, R.color.md_white_1000
)
)!!, 1f
), 1f
)
)
@ -89,7 +89,11 @@ class AppWidgetText : BaseAppWidget() {
// Home
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.media_titles, pendingIntent)
@ -132,7 +136,7 @@ class AppWidgetText : BaseAppWidget() {
App.getContext(), playPauseRes, ContextCompat.getColor(
App.getContext(), R.color.md_white_1000
)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -143,7 +147,7 @@ class AppWidgetText : BaseAppWidget() {
ContextCompat.getColor(
App.getContext(), R.color.md_white_1000
)
)!!, 1f
), 1f
)
)
appWidgetView.setImageViewBitmap(
@ -154,7 +158,7 @@ class AppWidgetText : BaseAppWidget() {
ContextCompat.getColor(
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.widget.RemoteViews
import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
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) {
PendingIntent.getForegroundService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
} 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.framework.CastSession
import com.google.android.gms.common.images.WebImage
import org.json.JSONObject
import java.net.MalformedURLException
import java.net.URL
@ -41,7 +42,7 @@ object CastHelper {
position,
MediaStatus.REPEAT_MODE_REPEAT_OFF,
progress,
null
JSONObject()
)
} catch (e: Exception) {
e.printStackTrace()

View file

@ -13,7 +13,7 @@ class ExpandedControlsActivity : ExpandedControllerActivity() {
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
super.onCreateOptionsMenu(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
}
}

View file

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

View file

@ -26,6 +26,8 @@ interface HistoryDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
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")
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 {
return HistoryEntity(
id = id,

View file

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

View file

@ -29,6 +29,7 @@ import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.addAccentColor
import code.name.monkey.retromusic.extensions.colorButtons
@ -138,7 +139,11 @@ class SleepTimerDialog : DialogFragment() {
}
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 {

View file

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

View file

@ -78,7 +78,7 @@ fun isBlack(fArr: FloatArray): Boolean {
fun isNearRedLine(fArr: FloatArray): Boolean {
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 {

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 kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import java.io.File
class LibraryViewModel(
private val repository: RealRepository
@ -43,7 +44,7 @@ class LibraryViewModel(
private val legacyPlaylists = MutableLiveData<List<Playlist>>()
private val genres = MutableLiveData<List<Genre>>()
private val searchResults = MutableLiveData<List<Any>>()
private val fabMargin = MutableLiveData<Int>(0)
private val fabMargin = MutableLiveData(0)
val paletteColor: LiveData<Int> = _paletteColor
init {
@ -227,7 +228,7 @@ class LibraryViewModel(
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 favoritePlaylist() = repository.favoritePlaylist()
suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song)
@ -272,6 +273,16 @@ class LibraryViewModel(
}
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 {
it.toSong()
})
@ -303,7 +314,21 @@ class LibraryViewModel(
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()

View file

@ -405,9 +405,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
binding.albumCoverContainer,
"${getString(R.string.transition_album_art)}_${album.id}"
)
startActivityForResult(
intent,
TAG_EDITOR_REQUEST, options.toBundle()
startActivity(
intent, options.toBundle()
)
return true
}
@ -499,8 +498,4 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
super.onDestroyView()
_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.extensions.navigate
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.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -131,11 +132,13 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
}
override fun loadLayoutRes(): Int {
return PreferenceUtil.albumGridStyle
return PreferenceUtil.albumGridStyle.layoutResId
}
override fun saveLayoutRes(layoutRes: Int) {
PreferenceUtil.albumGridStyle = layoutRes
PreferenceUtil.albumGridStyle = GridStyle.values().first { gridStyle ->
gridStyle.layoutResId == layoutRes
}
}
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_image -> R.layout.image
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
setAndSaveLayoutRes(layoutRes)
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.R
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.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -132,11 +132,13 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
}
override fun loadLayoutRes(): Int {
return PreferenceUtil.artistGridStyle
return PreferenceUtil.artistGridStyle.layoutResId
}
override fun saveLayoutRes(layoutRes: Int) {
PreferenceUtil.artistGridStyle = layoutRes
PreferenceUtil.artistGridStyle = GridStyle.values().first { gridStyle ->
gridStyle.layoutResId == layoutRes
}
}
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_image -> R.layout.image
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
setAndSaveLayoutRes(layoutRes)
return true

View file

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

View file

@ -37,6 +37,7 @@ import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.navOptions
import androidx.viewpager.widget.ViewPager
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
@ -151,7 +152,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return true
}
R.id.action_show_lyrics -> {
NavigationUtil.goToLyrics(requireActivity())
goToLyrics(requireActivity())
return true
}
R.id.action_equalizer -> {
@ -193,7 +194,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
private fun showLyricsIcon(item: MenuItem) {
val icon =
if (PreferenceUtil.showLyrics) R.drawable.ic_lyrics else R.drawable.ic_lyrics_outline
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable(
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
requireContext(),
icon,
toolbarIconColor()
@ -255,7 +256,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
} else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
}
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable(
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
requireContext(),
icon,
toolbarIconColor()
@ -394,7 +395,11 @@ fun goToArtist(activity: Activity) {
findNavController(R.id.fragment_container).navigate(
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(
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.databinding.FragmentFolderBinding
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.fragments.base.AbsMainActivityFragment
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> :
DialogAsyncTask<Params, Progress, Result> {
internal constructor(context: Context?) : super(context)
abstract class ListingFilesDialogAsyncTask<Params, Progress, Result> internal constructor(
context: Context?
) :
DialogAsyncTask<Params, Progress, Result>(context) {
override fun createDialog(context: Context): Dialog {
return MaterialAlertDialogBuilder(context)

View file

@ -162,30 +162,26 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
class FlingPlayBackController(context: Context) : View.OnTouchListener {
private var flingPlayBackController: GestureDetector
init {
flingPlayBackController = GestureDetector(context,
object : GestureDetector.SimpleOnGestureListener() {
override fun onFling(
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
if (abs(velocityX) > abs(velocityY)) {
if (velocityX < 0) {
MusicPlayerRemote.playNextSong()
return true
} else if (velocityX > 0) {
MusicPlayerRemote.playPreviousSong()
return true
}
private var flingPlayBackController = GestureDetector(context,
object : GestureDetector.SimpleOnGestureListener() {
override fun onFling(
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
if (abs(velocityX) > abs(velocityY)) {
if (velocityX < 0) {
MusicPlayerRemote.playNextSong()
return true
} else if (velocityX > 0) {
MusicPlayerRemote.playPreviousSong()
return true
}
return false
}
})
}
return false
}
})
@SuppressLint("ClickableViewAccessibility")
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.File
import java.io.FileOutputStream
import java.io.IOException
class UserInfoFragment : Fragment() {
@ -218,13 +217,13 @@ class UserInfoFragment : Fragment() {
val appDir = requireContext().filesDir
val file = File(appDir, fileName)
var successful = false
try {
kotlin.runCatching {
val os = BufferedOutputStream(FileOutputStream(file))
successful = ImageUtil.resizeBitmap(bitmap, 2048)
.compress(Bitmap.CompressFormat.WEBP, 100, os)
withContext(Dispatchers.IO) { os.close() }
} catch (e: IOException) {
e.printStackTrace()
}.onFailure {
it.printStackTrace()
}
if (successful) {
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.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToLyrics
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
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.ParallaxPagerTransformer
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.color.MediaNotificationProcessor
import kotlinx.coroutines.Dispatchers
@ -214,7 +214,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
}
// Go to lyrics activity when clicked lyrics
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 var audioVolumeObserver: AudioVolumeObserver? = null
private val audioManager: AudioManager?
private val audioManager: AudioManager
get() = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
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) {
val audioManager = audioManager
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0)
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0)
}
override fun onStartTrackingTouch(seekArc: SeekArc?) {

View file

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

View file

@ -316,7 +316,7 @@ class FullPlaybackControlsFragment :
fun updateIsFavorite(animate: Boolean = false) {
lifecycleScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
if (playlist != null) {
val song: SongEntity =
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
@ -327,7 +327,7 @@ class FullPlaybackControlsFragment :
} else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
}
val drawable: Drawable? = RetroUtil.getTintedVectorDrawable(
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
requireContext(),
icon,
Color.WHITE
@ -347,7 +347,7 @@ class FullPlaybackControlsFragment :
private fun toggleFavorite(song: Song) {
lifecycleScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId)
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) {
lifecycleScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
if (playlist != null) {
val song: SongEntity =
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.adapter.playlist.PlaylistAdapter
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.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
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.material.transition.MaterialSharedAxis
@ -42,7 +42,7 @@ class PlaylistsFragment :
super.onViewCreated(view, savedInstanceState)
libraryViewModel.getPlaylists().observe(viewLifecycleOwner, {
if (it.isNotEmpty())
adapter?.swapDataSet(it)
adapter?.swapDataSet(it.filter { playlistWithSongs-> playlistWithSongs.songs.isNotEmpty() })
else
adapter?.swapDataSet(listOf())
})
@ -66,9 +66,10 @@ class PlaylistsFragment :
}
override fun createAdapter(): PlaylistAdapter {
val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet
return PlaylistAdapter(
requireActivity(),
ArrayList(),
dataSet,
itemLayoutRes(),
null,
this
@ -77,7 +78,11 @@ class PlaylistsFragment :
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
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.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)
@ -89,12 +94,46 @@ class PlaylistsFragment :
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (handleGridSizeMenuItem(item)) {
return true
}
if (handleSortOrderMenuItem(item)) {
return true
}
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) {
val order: String? = getSortOrder()
@ -142,13 +181,32 @@ class PlaylistsFragment :
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) {
menu.add(0, id, 0, title).isChecked = checked
}
override fun setGridSize(gridSize: Int) {
TODO("Not yet implemented")
adapter?.notifyDataSetChanged()
}
override fun setSortOrder(sortOrder: String) {
@ -164,23 +222,23 @@ class PlaylistsFragment :
}
override fun loadGridSize(): Int {
return 2
return PreferenceUtil.playlistGridSize
}
override fun saveGridSize(gridColumns: Int) {
//Add grid save
PreferenceUtil.playlistGridSize = gridColumns
}
override fun loadGridSizeLand(): Int {
return 4
return PreferenceUtil.playlistGridSizeLand
}
override fun saveGridSizeLand(gridColumns: Int) {
//Add land grid save
PreferenceUtil.playlistGridSizeLand = gridColumns
}
override fun loadLayoutRes(): Int {
return R.layout.item_card
return R.layout.item_grid
}
override fun saveLayoutRes(layoutRes: Int) {

View file

@ -21,8 +21,8 @@ import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.GridLayoutManager
import code.name.monkey.retromusic.R
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.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -114,11 +114,13 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
@LayoutRes
override fun loadLayoutRes(): Int {
return PreferenceUtil.songGridStyle
return PreferenceUtil.songGridStyle.layoutResId
}
override fun saveLayoutRes(@LayoutRes layoutRes: Int) {
PreferenceUtil.songGridStyle = layoutRes
PreferenceUtil.songGridStyle = GridStyle.values().first { gridStyle ->
gridStyle.layoutResId == layoutRes
}
}
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_image -> R.layout.image
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
setAndSaveLayoutRes(layoutRes)
return true

View file

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

View file

@ -23,28 +23,33 @@ object BackupHelper {
zipItems.addAll(getDatabaseZipItems(context))
zipItems.addAll(getSettingsZipItems(context))
getUserImageZipItems(context)?.let { zipItems.addAll(it) }
withContext(Dispatchers.IO) {
zipAll(zipItems, backupFile)
}
zipItems.addAll(getCustomArtistZipItems(context))
zipAll(zipItems, backupFile)
}
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) {
try {
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
for (zipItem in zipItems) {
FileInputStream(zipItem.filePath).use { fi ->
BufferedInputStream(fi).use { origin ->
val entry = ZipEntry(zipItem.zipPath)
out.putNextEntry(entry)
origin.copyTo(out)
withContext(Dispatchers.IO) {
kotlin.runCatching {
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
for (zipItem in zipItems) {
FileInputStream(zipItem.filePath).use { fi ->
BufferedInputStream(fi).use { origin ->
val entry = ZipEntry(zipItem.zipPath)
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) {
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
Toast.makeText(App.getContext(), "Backup created successfully", Toast.LENGTH_SHORT)
.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?) {
withContext(Dispatchers.IO) {
ZipInputStream(inputStream).use {
@ -84,6 +111,16 @@ object BackupHelper {
if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry)
if (entry.isPreferenceEntry()) restorePreferences(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
}
}
@ -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 =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
.toString() + "/RetroMusic/Backups/"
@ -141,6 +220,7 @@ object BackupHelper {
private const val DATABASES_PATH = "databases"
private const val SETTINGS_PATH = "prefs"
private const val IMAGES_PATH = "userImages"
private const val CUSTOM_ARTISTS_PATH = "artistImages"
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
private fun ZipEntry.isDatabaseEntry(): Boolean {
@ -155,6 +235,14 @@ object BackupHelper {
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 {
return name.substring(name.lastIndexOf(File.separator))
}

View file

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

View file

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

View file

@ -134,7 +134,7 @@ class SortOrder {
companion object {
/* 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 */
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);
int cores = EXECUTOR_THREADS;
ArrayList<BlurTask> horizontal = new ArrayList<BlurTask>(cores);
ArrayList<BlurTask> vertical = new ArrayList<BlurTask>(cores);
ArrayList<BlurTask> horizontal = new ArrayList<>(cores);
ArrayList<BlurTask> vertical = new ArrayList<>(cores);
for (int i = 0; i < cores; i++) {
horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
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++) {
src[dst_i] =
(int)
((src[dst_i] & 0xFFFFFFFF)
((src[dst_i])
| ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16)
| ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8)
| ((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
@ -245,7 +245,7 @@ public class StackBlur {
for (y = 0; y < h; y++) {
src[dst_i] =
(int)
((src[dst_i] & 0xFFFFFFFF)
((src[dst_i])
| ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16)
| ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8)
| ((((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 FragmentTransaction mCurTransaction = null;
private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
private final ArrayList<Fragment> mFragments = new ArrayList<>();
private Fragment mCurrentPrimaryItem = null;
public CustomFragmentStatePagerAdapter(FragmentManager fm) {
@ -92,11 +92,11 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
public abstract Fragment getItem(int position);
@Override
public void startUpdate(ViewGroup container) {}
public void startUpdate(@NonNull ViewGroup container) {}
@NonNull
@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
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
@ -132,7 +132,7 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
}
@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;
if (mCurTransaction == null) {
@ -152,23 +152,21 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
}
@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;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
mCurrentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(ViewGroup container) {
public void finishUpdate(@NonNull ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
@ -177,7 +175,7 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
}
@Override
public boolean isViewFromObject(View view, Object object) {
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return ((Fragment) object).getView() == view;
}
@ -212,8 +210,8 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
mSavedState.clear();
mFragments.clear();
if (fss != null) {
for (int i = 0; i < fss.length; i++) {
mSavedState.add((Fragment.SavedState) fss[i]);
for (Parcelable parcelable : fss) {
mSavedState.add((Fragment.SavedState) parcelable);
}
}
Iterable<String> keys = bundle.keySet();

View file

@ -40,8 +40,6 @@ abstract class WrappedAsyncTaskLoader<D>
if (!isReset) {
this.mData = 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
}
override fun getPageTitle(position: Int): CharSequence? {
override fun getPageTitle(position: Int): CharSequence {
return context.getString(values()[position].titleRes)
}
}

View file

@ -18,6 +18,7 @@ import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import androidx.appcompat.app.AlertDialog
import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN
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.R
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.colorControlNormal
import code.name.monkey.retromusic.extensions.materialDialog
@ -69,11 +71,7 @@ class BlacklistPreferenceDialog : DialogFragment(), BlacklistFolderChooserDialog
.setNeutralButton(R.string.clear_action) { _, _ ->
materialDialog(R.string.clear_blacklist)
.setMessage(R.string.do_you_want_to_clear_the_blacklist)
.setPositiveButton(R.string.clear_action) { _, _ ->
BlacklistStore.getInstance(
requireContext()
).clear()
}
.setPositiveButton(R.string.clear_action, null)
.setNegativeButton(android.R.string.cancel, null)
.create()
.colorButtons()
@ -107,7 +105,21 @@ class BlacklistPreferenceDialog : DialogFragment(), BlacklistFolderChooserDialog
.colorButtons()
.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>

View file

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

View file

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

View file

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

View file

@ -15,6 +15,7 @@
package code.name.monkey.retromusic.repository
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.model.Album
import code.name.monkey.retromusic.model.Artist
@ -110,7 +111,8 @@ class RealArtistRepository(
songRepository.makeSongCursor(
null,
null,
getSongLoaderSortOrder()
"lower($ALBUM_ARTIST)" +
if (PreferenceUtil.artistSortOrder == SortOrder.ArtistSortOrder.ARTIST_A_Z) "" else " DESC"
)
)
return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs))
@ -155,12 +157,6 @@ class RealArtistRepository(
} else {
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 updateSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity)
suspend fun deleteSongInHistory(songId: Long)
suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity>
suspend fun playCountSongs(): List<PlayCountEntity>
suspend fun blackListPaths(): List<BlackListStoreEntity>
@ -323,6 +324,9 @@ class RealRepository(
override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) =
roomRepository.deleteSongInPlayCount(playCountEntity)
override suspend fun deleteSongInHistory(songId: Long) =
roomRepository.deleteSongInHistory(songId)
override suspend fun checkSongExistInPlayCount(songId: Long): List<PlayCountEntity> =
roomRepository.checkSongExistInPlayCount(songId)

View file

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

View file

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

View file

@ -75,6 +75,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Random;
import code.name.monkey.appthemehelper.util.VersionUtils;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.activities.LockScreenActivity;
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> playingQueue = new ArrayList<>();
private boolean pausedByTransientLossOfFocus;
private AudioVolumeObserver audioVolumeObserver = null;
private final BroadcastReceiver becomingNoisyReceiver =
new BroadcastReceiver() {
@ -453,7 +453,7 @@ public class MusicService extends MediaBrowserServiceCompat
.registerContentObserver(
MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
audioVolumeObserver = new AudioVolumeObserver(this);
AudioVolumeObserver audioVolumeObserver = new AudioVolumeObserver(this);
audioVolumeObserver.register(AudioManager.STREAM_MUSIC, this);
PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this);
@ -837,8 +837,7 @@ public class MusicService extends MediaBrowserServiceCompat
// Request from an untrusted package: return an empty browser root
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null);
} 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.
*/
boolean isRecentRequest = false;
@ -1157,7 +1156,7 @@ public class MusicService extends MediaBrowserServiceCompat
playback.setNextDataSource(getTrackUri(Objects.requireNonNull(getSongAt(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())
.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()) {
final Point screenSize = RetroUtil.getScreenSize(MusicService.this);
@ -1595,11 +1592,8 @@ public class MusicService extends MediaBrowserServiceCompat
mediaButtonIntent.setComponent(mediaButtonReceiverComponentName);
PendingIntent mediaButtonReceiverPendingIntent;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, PendingIntent.FLAG_MUTABLE);
} else {
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
}
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent,
VersionUtils.INSTANCE.hasMarshmallow() ? PendingIntent.FLAG_IMMUTABLE : 0);
mediaSession = new MediaSessionCompat(
this,
@ -1608,9 +1602,6 @@ public class MusicService extends MediaBrowserServiceCompat
mediaButtonReceiverPendingIntent);
MediaSessionCallback mediasessionCallback =
new MediaSessionCallback(getApplicationContext(), this);
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(mediasessionCallback);
mediaSession.setActive(true);
mediaSession.setMediaButtonReceiver(mediaButtonReceiverPendingIntent);

View file

@ -38,6 +38,6 @@ class ThrottledSeekHandler(
companion object {
// 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.NotificationManager
import android.content.Context.NOTIFICATION_SERVICE
import android.content.pm.ServiceInfo
import android.os.Build
import androidx.annotation.RequiresApi
import code.name.monkey.retromusic.R
@ -62,7 +63,15 @@ abstract class PlayingNotification {
}
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) {
notificationManager!!.notify(NOTIFICATION_ID, notification)
}

View file

@ -25,6 +25,7 @@ import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.text.HtmlCompat
import androidx.media.app.NotificationCompat.MediaStyle
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.db.PlaylistEntity
@ -53,7 +54,7 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
stopped = false
GlobalScope.launch {
val song = service.currentSong
val playlist: PlaylistEntity? = MusicUtil.repository.favoritePlaylist()
val playlist: PlaylistEntity = MusicUtil.repository.favoritePlaylist()
val isPlaying = service.isPlaying
val isFavorite = if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId)
@ -69,11 +70,11 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val clickIntent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getActivity(service, 0, action, PendingIntent.FLAG_IMMUTABLE)
} else {
PendingIntent.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT)
}
PendingIntent.getActivity(
service, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0 or PendingIntent.FLAG_UPDATE_CURRENT
)
val serviceName = ComponentName(service, MusicService::class.java)
val intent = Intent(ACTION_QUIT)
@ -82,7 +83,9 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
service,
0,
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
.getDimensionPixelSize(R.dimen.notification_big_image_size)
@ -171,18 +174,16 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
.addAction(playPauseAction)
.addAction(nextAction)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setStyle(
MediaStyle()
.setMediaSession(service.mediaSession.sessionToken)
.setShowActionsInCompactView(1, 2, 3)
)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (Build.VERSION.SDK_INT <=
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
) {
builder.color = color
}
builder.setStyle(
MediaStyle()
.setMediaSession(service.mediaSession.sessionToken)
.setShowActionsInCompactView(1, 2, 3)
)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (Build.VERSION.SDK_INT <=
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
) {
builder.color = color
}
if (stopped) {
@ -199,6 +200,10 @@ class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
val serviceName = ComponentName(service, MusicService::class.java)
val intent = Intent(action)
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,
0,
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)
@ -181,21 +183,21 @@ class PlayingNotificationOreo : PlayingNotification() {
service,
R.drawable.ic_close,
primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val prev = createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_skip_previous_round_white_32dp,
primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val next = createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_skip_next_round_white_32dp,
primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val playPause = createBitmap(
RetroUtil.getTintedVectorDrawable(
@ -204,7 +206,7 @@ class PlayingNotificationOreo : PlayingNotification() {
R.drawable.ic_pause_white_48dp
else
R.drawable.ic_play_arrow_white_48dp, primary
)!!, NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
notificationLayout.setTextColor(R.id.title, primary)
@ -231,7 +233,7 @@ class PlayingNotificationOreo : PlayingNotification() {
service,
R.drawable.ic_notification,
secondary
)!!, 0.6f
), 0.6f
)
)
notificationLayoutBig.setImageViewBitmap(
@ -241,7 +243,7 @@ class PlayingNotificationOreo : PlayingNotification() {
service,
R.drawable.ic_notification,
secondary
)!!, 0.6f
), 0.6f
)
)
@ -262,7 +264,11 @@ class PlayingNotificationOreo : PlayingNotification() {
): PendingIntent {
val intent = Intent(action)
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 androidx.viewpager.widget.ViewPager
import kotlin.math.abs
class DepthTransformation : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.alpha = 0f
} else if (position <= 0) { // [-1,0]
page.alpha = 1f
page.translationX = 0f
page.scaleX = 1f
page.scaleY = 1f
} else if (position <= 1) { // (0,1]
page.translationX = -position * page.width
page.alpha = 1 - Math.abs(position)
page.scaleX = 1 - Math.abs(position)
page.scaleY = 1 - Math.abs(position)
} else { // (1,+Infinity]
// This page is way off-screen to the right.
page.alpha = 0f
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
page.alpha = 0f
}
position <= 0 -> { // [-1,0]
page.alpha = 1f
page.translationX = 0f
page.scaleX = 1f
page.scaleY = 1f
}
position <= 1 -> { // (0,1]
page.translationX = -position * page.width
page.alpha = 1 - abs(position)
page.scaleX = 1 - abs(position)
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 androidx.viewpager.widget.ViewPager
import kotlin.math.abs
class HorizontalFlipTransformation : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) {
@ -30,21 +31,23 @@ class HorizontalFlipTransformation : ViewPager.PageTransformer {
}
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.alpha = 0f
} else if (position <= 0) { // [-1,0]
page.alpha = 1f
page.rotationX = 180 * (1 - Math.abs(position) + 1)
} else if (position <= 1) { // (0,1]
page.alpha = 1f
page.rotationX = -180 * (1 - Math.abs(position) + 1)
} else { // (1,+Infinity]
// This page is way off-screen to the right.
page.alpha = 0f
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
page.alpha = 0f
}
position <= 0 -> { // [-1,0]
page.alpha = 1f
page.rotationX = 180 * (1 - abs(position) + 1)
}
position <= 1 -> { // (0,1]
page.alpha = 1f
page.rotationX = -180 * (1 - abs(position) + 1)
}
else -> { // (1,+Infinity]
// 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 androidx.viewpager.widget.ViewPager
import kotlin.math.abs
import kotlin.math.max
/**
* @author Hemanth S (h4h13).
@ -27,37 +29,41 @@ class NormalPageTransformer : ViewPager.PageTransformer {
val pageWidth = view.width
val pageHeight = view.height
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.alpha = 1f
view.scaleY = 0.7f
} else if (position <= 1) { // [-1,1]
// 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
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
view.alpha = 1f
view.scaleY = 0.7f
}
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)
view.scaleX = scaleFactor
view.scaleY = scaleFactor
// Scale the page down (between MIN_SCALE and 1)
view.scaleX = scaleFactor
view.scaleY = scaleFactor
// Fade the page relative to its size.
//view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
// Fade the page relative to its size.
//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.
view.alpha = 1f
view.scaleY = 0.7f
}
else -> { // (1,+Infinity]
// This page is way off-screen to the right.
view.alpha = 1f
view.scaleY = 0.7f
}
}
}
companion object {
private val MIN_SCALE = 0.85f
private val MIN_ALPHA = 0.5f
private const val MIN_SCALE = 0.85f
private const val MIN_ALPHA = 0.5f
}
}

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.transform
import android.view.View
import androidx.viewpager.widget.ViewPager
import kotlin.math.abs
class VerticalFlipTransformation : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) {
@ -30,22 +31,24 @@ class VerticalFlipTransformation : ViewPager.PageTransformer {
}
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.alpha = 0f
} else if (position <= 0) { // [-1,0]
page.alpha = 1f
page.rotationY = 180 * (1 - Math.abs(position) + 1)
} else if (position <= 1) { // (0,1]
page.alpha = 1f
page.rotationY = -180 * (1 - Math.abs(position) + 1)
} else { // (1,+Infinity]
// This page is way off-screen to the right.
page.alpha = 0f
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
page.alpha = 0f
}
position <= 0 -> { // [-1,0]
page.alpha = 1f
page.rotationY = 180 * (1 - abs(position) + 1)
}
position <= 1 -> { // (0,1]
page.alpha = 1f
page.rotationY = -180 * (1 - abs(position) + 1)
}
else -> { // (1,+Infinity]
// 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.graphics.Bitmap;
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;
@ -20,19 +15,12 @@ import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.model.Song;
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(
Context context, List<Song> songPlaylist, boolean round, boolean blur) {
if (songPlaylist == null || songPlaylist.isEmpty()) return getDefaultBitmap(context, round);
if (songPlaylist.size() == 1) return getBitmapWithAlbumId(context, songPlaylist.get(0).getAlbumId());
Context context, List<Song> songPlaylist) {
if (songPlaylist == null || songPlaylist.isEmpty()) return getDefaultBitmap(context);
if (songPlaylist.size() == 1)
return getBitmapWithAlbumId(context, songPlaylist.get(0).getAlbumId());
List<Long> albumID = new ArrayList<>();
for (Song song : songPlaylist) {
if (!albumID.contains(song.getAlbumId())) albumID.add(song.getAlbumId());
@ -40,108 +28,12 @@ public class AutoGeneratedPlaylistBitmap {
List<Bitmap> art = new ArrayList<>();
for (Long id : albumID) {
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;
}
return MergedImageUtils.INSTANCE.joinImages(art);
}
private static Bitmap getBitmapCollection(ArrayList<Bitmap> art, boolean round) {
long start = System.currentTimeMillis();
// lấy kích thước 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) {
try {
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);
}
}

View file

@ -594,7 +594,7 @@ public final class BitmapEditor {
/**
* 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 newWidth (new desired Width)
* @return image (new resized image)
@ -609,22 +609,21 @@ public final class BitmapEditor {
// onTap the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(image, 0, 0, width, height, matrix, false);
return resizedBitmap;
return Bitmap.createBitmap(image, 0, 0, width, height, matrix, false);
}
public static boolean TrueIfBitmapBigger(Bitmap bitmap, int size) {
int sizeBitmap =
(bitmap.getHeight() > bitmap.getWidth()) ? bitmap.getHeight() : bitmap.getWidth();
Math.max(bitmap.getHeight(), bitmap.getWidth());
return sizeBitmap > size;
}
public static Bitmap GetRoundedBitmapWithBlurShadow(
public static Bitmap getRoundedBitmapWithBlurShadow(
Bitmap original, int paddingTop, int paddingBottom, int paddingLeft, int paddingRight) {
int original_width = original.getWidth();
int orginal_height = original.getHeight();
int original_height = original.getHeight();
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);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();

View file

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

View file

@ -1,6 +1,9 @@
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
@ -75,25 +78,12 @@ internal object MergedImageUtils {
val bit = Bitmap.createScaledBitmap(bitmap, onePartSize, onePartSize, true)
canvas.drawBitmap(
bit,
(onePartSize * (i % parts)).toFloat(),
(onePartSize * (i / parts)).toFloat(),
(onePartSize * (i % parts)).toFloat() + (i % 3) * 50,
(onePartSize * (i / parts)).toFloat() + (i / 3) * 50,
paint
)
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
}

View file

@ -348,7 +348,7 @@ object MusicUtil : KoinComponent {
val repository = get<Repository>()
fun toggleFavorite(context: Context, song: Song) {
GlobalScope.launch {
val playlist: PlaylistEntity? = repository.favoritePlaylist()
val playlist: PlaylistEntity = repository.favoritePlaylist()
if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
@ -529,7 +529,7 @@ object MusicUtil : KoinComponent {
val pendingIntent = MediaStore.createDeleteRequest(activity.contentResolver, songs.map {
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 {

View file

@ -20,12 +20,10 @@ import android.content.Intent
import android.media.audiofx.AudioEffect
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.navigation.findNavController
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.*
import code.name.monkey.retromusic.activities.bugreport.BugReportActivity
import code.name.monkey.retromusic.helper.MusicPlayerRemote.audioSessionId
import com.google.android.material.bottomsheet.BottomSheetBehavior
object NavigationUtil {
fun bugReport(activity: Activity) {
@ -40,21 +38,6 @@ object NavigationUtil {
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) {
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.
*/
@Suppress("Deprecation")
@SuppressLint("PackageManagerGetSignatures")
private fun getPackageInfo(callingPackage: String): PackageInfo? =
packageManager.getPackageInfo(callingPackage,
@ -208,11 +209,11 @@ class PackageValidator(
private fun getSignature(packageInfo: PackageInfo): String? {
// 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.
if (packageInfo.signatures == null || packageInfo.signatures.size != 1) {
return null
return if (packageInfo.signatures == null || packageInfo.signatures.size != 1) {
null
} else {
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.db.PlaylistWithSongs;
import code.name.monkey.retromusic.helper.M3UConstants;
import code.name.monkey.retromusic.helper.M3UWriter;
import code.name.monkey.retromusic.model.Playlist;
import code.name.monkey.retromusic.model.PlaylistSong;
@ -167,8 +166,8 @@ public class PlaylistsUtil {
Toast.LENGTH_SHORT)
.show();
}
} catch (SecurityException ignored) {
ignored.printStackTrace();
} catch (SecurityException exception) {
exception.printStackTrace();
}
}
@ -235,13 +234,13 @@ public class PlaylistsUtil {
for (int i = 0; i < selectionArgs.length; i++) {
selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList());
}
String selection = MediaStore.Audio.Playlists.Members._ID + " in (";
//noinspection unused
for (String selectionArg : selectionArgs) selection += "?, ";
selection = selection.substring(0, selection.length() - 2) + ")";
StringBuilder selection = new StringBuilder(MediaStore.Audio.Playlists.Members._ID + " in (");
for (String selectionArg : selectionArgs) selection.append("?, ");
selection = new StringBuilder(selection.substring(0, selection.length() - 2) + ")");
try {
context.getContentResolver().delete(uri, selection, selectionArgs);
context.getContentResolver().delete(uri, selection.toString(), selectionArgs);
} 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.getStringOrDefault
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.folder.FoldersFragment
import code.name.monkey.retromusic.helper.SortOrder.*
@ -344,22 +345,40 @@ object PreferenceUtil {
putInt(LYRICS_OPTIONS, value)
}
var songGridStyle
get() = sharedPreferences.getInt(SONG_GRID_STYLE, R.layout.item_grid)
var songGridStyle: GridStyle
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 {
putInt(SONG_GRID_STYLE, value)
putInt(SONG_GRID_STYLE, value.id)
}
var albumGridStyle
get() = sharedPreferences.getInt(ALBUM_GRID_STYLE, R.layout.item_grid)
var albumGridStyle: GridStyle
get() {
val id: Int = sharedPreferences.getInt(ALBUM_GRID_STYLE, 0)
return GridStyle.values().firstOrNull { gridStyle ->
gridStyle.id == id
} ?: GridStyle.Grid
}
set(value) = sharedPreferences.edit {
putInt(ALBUM_GRID_STYLE, value)
putInt(ALBUM_GRID_STYLE, value.id)
}
var artistGridStyle
get() = sharedPreferences.getInt(ARTIST_GRID_STYLE, R.layout.item_grid_circle)
var artistGridStyle: GridStyle
get() {
val id: Int = sharedPreferences.getInt(ARTIST_GRID_STYLE, 4)
return GridStyle.values().firstOrNull { gridStyle ->
gridStyle.id == id
} ?: GridStyle.Circular
}
set(value) = sharedPreferences.edit {
putInt(ARTIST_GRID_STYLE, value)
putInt(ARTIST_GRID_STYLE, value.id)
}
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
get() {
val id: Int = sharedPreferences.getInt(ALBUM_COVER_STYLE, 0)

View file

@ -42,7 +42,7 @@ public class RetroColorUtil {
float[] hsv = new float[3];
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);
}
@ -188,7 +188,7 @@ public class RetroColorUtil {
public static int getDominantColor(Bitmap bitmap, int defaultFooterColor) {
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(
swatches, (swatch1, swatch2) -> swatch2.getPopulation() - swatch1.getPopulation());
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.NonNull;
import androidx.annotation.Nullable;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import androidx.core.content.res.ResourcesCompat;
import java.net.InetAddress;
import java.net.NetworkInterface;
@ -127,14 +127,14 @@ public class RetroUtil {
return result;
}
@Nullable
@NonNull
public static Drawable getTintedVectorDrawable(
@NonNull Context context, @DrawableRes int id, @ColorInt int color) {
return TintHelper.createTintedDrawable(
getVectorDrawable(context.getResources(), id, context.getTheme()), color);
}
@Nullable
@NonNull
public static Drawable getTintedVectorDrawable(
@NonNull Resources res,
@DrawableRes int resId,
@ -146,10 +146,7 @@ public class RetroUtil {
@Nullable
public static Drawable getVectorDrawable(
@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) {
if (Build.VERSION.SDK_INT >= 21) {
return res.getDrawable(resId, theme);
}
return VectorDrawableCompat.create(res, resId, theme);
return ResourcesCompat.getDrawable(res, resId, theme);
}
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 boolean isSAFRequired(File file) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !file.canWrite();
return !file.canWrite();
}
public static boolean isSAFRequired(String path) {

View file

@ -29,22 +29,22 @@ public class SwipeAndDragHelper extends ItemTouchHelper.Callback {
}
@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;
return makeMovementFlags(dragFlags, 0);
}
@Override
public boolean onMove(
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
@NonNull RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
contract.onViewMoved(viewHolder.getLayoutPosition(), target.getLayoutPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {}
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {}
@Override
public boolean isLongPressDragEnabled() {
@ -53,13 +53,13 @@ public class SwipeAndDragHelper extends ItemTouchHelper.Callback {
@Override
public void onChildDraw(
Canvas c,
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
float dX,
float dY,
int actionState,
boolean isCurrentlyActive) {
@NonNull Canvas c,
@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
float dX,
float dY,
int actionState,
boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
float alpha = 1 - (Math.abs(dX) / recyclerView.getWidth());
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 {
val tx = (v.translationX + 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 String TAG = "ColorPicking";
private float[] mFilteredBackgroundHsl = null;
private final Palette.Filter mBlackWhiteFilter =
(rgb, hsl) -> !isWhiteOrBlack(hsl);
private boolean mIsLowPriority;
private int backgroundColor;
private int secondaryTextColor;
private int primaryTextColor;
@ -376,10 +374,6 @@ public class MediaNotificationProcessor {
return hslColor[2] >= WHITE_MIN_LIGHTNESS;
}
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
}
private void ensureColors(int backgroundColor, int mForegroundColor) {
{
double backLum = NotificationColorUtil.calculateLuminance(backgroundColor);

View file

@ -55,7 +55,7 @@ public class NotificationColorUtil {
private final ImageUtils mImageUtils = new ImageUtils();
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)
@ -353,11 +353,7 @@ public class NotificationColorUtil {
public static int resolvePrimaryColor(Context context, int backgroundColor) {
boolean useDark = shouldUseDark(backgroundColor);
if (useDark) {
return ContextCompat.getColor(context, android.R.color.primary_text_light);
} else {
return ContextCompat.getColor(context, android.R.color.primary_text_light);
}
return ContextCompat.getColor(context, android.R.color.primary_text_light);
}
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) {
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) {
return amount < low ? low : (amount > high ? high : amount);
return amount < low ? low : (Math.min(amount, high));
}
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();
}
@NonNull
@Override
public String toString() {
return "Crumb{" + "file=" + file + ", scrollPos=" + scrollPos + '}';

View file

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

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