Merge pull request #1226 from prathameshmm02/dev

New features, improvements and bug fixes
This commit is contained in:
Daksh P. Jain 2022-01-24 17:06:52 +05:30 committed by GitHub
commit b74b4808c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
328 changed files with 3803 additions and 26387 deletions

View file

@ -14,8 +14,8 @@ android {
vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic"
versionCode 10557
versionName '5.6.1'
versionCode 10563
versionName '5.7.0'
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
}
@ -88,14 +88,13 @@ static def getDate() {
new Date().format('MMddyyyyss')
}
dependencies {
implementation project(':appthemehelper')
implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01'
implementation "androidx.preference:preference-ktx:$preference_version"
implementation 'androidx.core:core-ktx:1.7.0'
@ -103,7 +102,7 @@ dependencies {
//Cast Dependencies
implementation 'androidx.mediarouter:mediarouter:1.2.5'
implementation 'com.google.android.gms:play-services-cast-framework:21.0.0'
implementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
//WebServer by NanoHttpd
implementation "org.nanohttpd:nanohttpd:2.3.1"
@ -111,7 +110,7 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
def room_version = '2.4.0'
def room_version = '2.4.1'
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
@ -143,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.4'
def koin_version = '3.1.5'
implementation "io.insert-koin:koin-core:$koin_version"
implementation "io.insert-koin:koin-android:$koin_version"
@ -160,6 +159,8 @@ dependencies {
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC3'
implementation 'com.github.jetradarmobile:android-snowfall:1.2.1'
implementation "dev.chrisbanes.insetter:insetter:0.6.1"
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
implementation 'com.github.Adonai:jaudiotagger:2.3.15'
implementation 'com.anjlab.android.iab.v3:library:2.0.3'

View file

@ -292,6 +292,18 @@
android:name="android.appwidget.provider"
android:resource="@xml/app_widget_md3_info" />
</receiver>
<receiver
android:name=".appwidgets.AppWidgetCircle"
android:exported="true"
android:label="@string/app_widget_circle_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/app_widget_circle_info" />
</receiver>
<service
android:name=".service.MusicService"

View file

@ -70,6 +70,8 @@
title="KeyboardVisibilityEvent">KeyboardVisibilityEvent</a></b> by Yasuhiro SHIMIZU</p>
<p><b><a href="https://github.com/JetradarMobile/android-snowfall"
title="android-snowfall">android-snowfall</a></b> by Jetradar Mobile</p>
<p><b><a href="https://github.com/chrisbanes/insetter"
title="Insetter">Insetter</a></b> by Chris Banes</p>
<p><b><a href="https://materialdesignicons.com" title="Icons"> Icons</a></b> by Austin Andrews</p>
<p><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b>
</p>

View file

@ -58,12 +58,41 @@
}
{style-placeholder}
</style>
</head>
<body>
<div>
<h5>December 28, 2021</h5>
<h5>January 25, 2022</h5>
<h2>v5.7.0</h2>
<h3>What's New</h3>
<ul>
<li>Added accent color extraction on Android 8.1+ devices</li>
<li>Added new Circle widget</li>
<li>Added Collapsing appbar to library tabs with an option to switch back to simple appbar
</li>
<li>Added Search tab</li>
<li>Added option to use circular play button</li>
<li>Added lyrics editing on A11+ devices again</li>
<li>Added Long Press to forward, rewind current song</li>
<li>Added ability to set Playback speed and pitch</li>
<li>Added option to show lyrics over Cover</li>
<li>Added option to keep screen on when showing lyrics</li>
<li>Added option to switch to Manrope font</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Fixed Gapless Playback</li>
<li>Fixed Shuffle FAB going behind Mini Player</li>
<li>Fixed crashes on Pre-marshmallow devices</li>
<li>Blacklisted songs can't be played after opening from outside the app</li>
<li>Fixed various small bugs and some minor improvements</li>
</ul>
</div>
<div>
<h5>January 1, 2021</h5>
<h2>v5.6.1</h2>
<h3>Fixed</h3>
<ul>

View file

@ -20,6 +20,7 @@ import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import code.name.monkey.retromusic.helper.WallpaperAccentManager
import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.PurchaseInfo
import org.koin.android.ext.koin.androidContext
@ -28,6 +29,7 @@ import org.koin.core.context.startKoin
class App : Application() {
lateinit var billingProcessor: BillingProcessor
private val wallpaperAccentManager = WallpaperAccentManager(this)
override fun onCreate() {
super.onCreate()
@ -44,6 +46,7 @@ class App : Application() {
.coloredNavigationBar(true)
.commit()
}
wallpaperAccentManager.init()
if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).initDynamicShortcuts()
@ -71,6 +74,7 @@ class App : Application() {
override fun onTerminate() {
super.onTerminate()
billingProcessor.release()
wallpaperAccentManager.release()
}
companion object {

View file

@ -73,7 +73,7 @@ const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
const val CAROUSEL_EFFECT = "carousel_effect"
const val COLORED_NOTIFICATION = "colored_notification"
const val CLASSIC_NOTIFICATION = "classic_notification"
const val GAP_LESS_PLAYBACK = "gap_less_playback"
const val GAP_LESS_PLAYBACK = "gapless_playback"
const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen"
const val BLURRED_ALBUM_ART = "blurred_album_art"
const val NEW_BLUR_AMOUNT = "new_blur_amount"
@ -154,3 +154,11 @@ const val LAST_USED_TAB = "last_used_tab"
const val WHITELIST_MUSIC = "whitelist_music"
const val MATERIAL_YOU = "material_you"
const val SNOWFALL = "snowfall"
const val LYRICS_TYPE = "lyrics_type"
const val PLAYBACK_SPEED = "playback_speed"
const val PLAYBACK_PITCH = "playback_pitch"
const val CUSTOM_FONT = "custom_font"
const val APPBAR_MODE = "appbar_mode"
const val WALLPAPER_ACCENT = "wallpaper_accent"
const val SCREEN_ON_LYRICS = "screen_on_lyrics"
const val CIRCLE_PLAY_BUTTON = "circle_play_button"

View file

@ -103,7 +103,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
currentFragment(R.id.fragment_container)?.enterTransition = null
}
when (destination.id) {
R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre -> {
R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre, R.id.action_search -> {
// Save the last tab
if (PreferenceUtil.rememberLastTab) {
saveTab(destination.id)
@ -147,7 +147,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == GENERAL_THEME || key == MATERIAL_YOU || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES) {
if (key == GENERAL_THEME || key == MATERIAL_YOU || key == WALLPAPER_ACCENT || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES || key == CUSTOM_FONT || key == APPBAR_MODE || key == CIRCLE_PLAY_BUTTON) {
postRecreate()
}
}

View file

@ -19,12 +19,9 @@ import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.LayoutRes
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager
@ -37,6 +34,7 @@ import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.databinding.ActivityDonationBinding
import code.name.monkey.retromusic.databinding.ItemDonationOptionBinding
import code.name.monkey.retromusic.extensions.*
import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.PurchaseInfo
@ -169,8 +167,8 @@ class SkuDetailsAdapter(
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(donationsDialog).inflate(
LAYOUT_RES_ID,
ItemDonationOptionBinding.inflate(
LayoutInflater.from(donationsDialog),
viewGroup,
false
)
@ -179,12 +177,14 @@ class SkuDetailsAdapter(
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
val skuDetails = skuDetailsList[i]
viewHolder.title.text = skuDetails.title.replace("Music Player - MP3 Player - Retro", "")
with(viewHolder.binding) {
itemTitle.text = skuDetails.title.replace("Music Player - MP3 Player - Retro", "")
.trim { it <= ' ' }
viewHolder.text.text = skuDetails.description
viewHolder.text.isVisible = false
viewHolder.price.text = skuDetails.priceText
viewHolder.image.setImageResource(getIcon(i))
itemText.text = skuDetails.description
itemText.isVisible = false
itemPrice.text = skuDetails.priceText
itemImage.setImageResource(getIcon(i))
}
val purchased = donationsDialog.billingProcessor!!.isPurchased(skuDetails.productId)
val titleTextColor = if (purchased) ATHUtil.resolveColor(
@ -194,13 +194,14 @@ class SkuDetailsAdapter(
val contentTextColor =
if (purchased) titleTextColor else donationsDialog.textColorSecondary()
viewHolder.title.setTextColor(titleTextColor)
viewHolder.text.setTextColor(contentTextColor)
viewHolder.price.setTextColor(titleTextColor)
strikeThrough(viewHolder.title, purchased)
strikeThrough(viewHolder.text, purchased)
strikeThrough(viewHolder.price, purchased)
with(viewHolder.binding) {
itemTitle.setTextColor(titleTextColor)
itemText.setTextColor(contentTextColor)
itemPrice.setTextColor(titleTextColor)
strikeThrough(itemTitle, purchased)
strikeThrough(itemText, purchased)
strikeThrough(itemPrice, purchased)
}
viewHolder.itemView.setOnTouchListener { _, _ -> purchased }
viewHolder.itemView.setOnClickListener { donationsDialog.donate(i) }
@ -210,17 +211,9 @@ class SkuDetailsAdapter(
return skuDetailsList.size
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var title: TextView = view.findViewById(R.id.itemTitle)
var text: TextView = view.findViewById(R.id.itemText)
var price: TextView = view.findViewById(R.id.itemPrice)
var image: AppCompatImageView = view.findViewById(R.id.itemImage)
}
class ViewHolder(val binding: ItemDonationOptionBinding) : RecyclerView.ViewHolder(binding.root)
companion object {
@LayoutRes
private val LAYOUT_RES_ID = R.layout.item_donation_option
private fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
textView.paintFlags =
if (strikeThrough) textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG

View file

@ -27,7 +27,6 @@ class WhatsNewActivity : AbsThemeActivity() {
super.onCreate(savedInstanceState)
val binding = ActivityWhatsNewBinding.inflate(layoutInflater)
setContentView(binding.root)
setLightStatusBarAuto(surfaceColor())
setTaskDescriptionColorAuto()
binding.toolbar.setNavigationOnClickListener { onBackPressed() }
ToolbarContentTintHelper.colorBackButton(binding.toolbar)

View file

@ -31,7 +31,7 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
@ -110,9 +110,15 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
when (newState) {
STATE_EXPANDED -> {
onPanelExpanded()
if (PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) {
keepScreenOn(true)
}
}
STATE_COLLAPSED -> {
onPanelCollapsed()
if ((PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) || !PreferenceUtil.isScreenOnEnabled) {
keepScreenOn(false)
}
}
STATE_SETTLING, STATE_DRAGGING -> {
if (fromNotification) {
@ -140,7 +146,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
insets
}
if (RetroUtil.isLandscape()) {
binding.slidingPanel.drawAboveSystemBarsWithPadding(true)
binding.slidingPanel.drawAboveSystemBarsWithPadding()
}
chooseFragmentForTheme()
setupSlidingUpPanel()
@ -199,6 +205,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
}
private fun animateNavigationBarColor(color: Int) {
if (VersionUtils.hasOreo()) return
navigationBarColorAnimator?.cancel()
navigationBarColorAnimator = ValueAnimator
.ofArgb(window.navigationBarColor, color).apply {
@ -217,14 +224,16 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
setMiniPlayerAlphaProgress(0F)
// restore values
animateNavigationBarColor(surfaceColor())
setLightStatusBarAuto(surfaceColor())
setLightNavigationAuto()
setLightStatusBarAuto()
setLightNavigationBarAuto()
setTaskDescriptionColor(taskColor)
playerFragment?.onHide()
}
open fun onPanelExpanded() {
setMiniPlayerAlphaProgress(1F)
onPaletteColorChanged()
playerFragment?.onShow()
}
private fun setupSlidingUpPanel() {
@ -291,7 +300,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
if (panelState == STATE_EXPANDED) {
navigationBarColor = surfaceColor()
setTaskDescColor(paletteColor)
val isColorLight = ColorUtil.isColorLight(paletteColor)
val isColorLight = paletteColor.isColorLight
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat)) {
setLightNavigationBar(true)
setLightStatusBar(isColorLight)
@ -314,13 +323,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
setLightStatusBar(false)
} else if (nowPlayingScreen == Fit) {
setLightStatusBar(false)
} else {
setLightStatusBar(
ColorUtil.isColorLight(
surfaceColor()
)
)
setLightNavigationBar(true)
}
}
}
@ -401,12 +403,10 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
bottomSheetBehavior.peekHeight = -windowInsets.safeGetBottomInsets()
bottomSheetBehavior.state = STATE_COLLAPSED
libraryViewModel.setFabMargin(if (isBottomNavVisible) dip(R.dimen.bottom_nav_height) else 0)
binding.slidingPanel.elevation = 0F
binding.bottomNavigationView.elevation = 10F
} else {
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
binding.slidingPanel.elevation = 0F
binding.bottomNavigationView.elevation = 10F
binding.bottomNavigationView.elevation = 5F
if (isBottomNavVisible) {
println("List")
if (animate) {

View file

@ -23,11 +23,12 @@ import android.view.View
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.core.os.ConfigurationCompat
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.LanguageContextWrapper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.theme.ThemeManager
import com.google.android.material.color.DynamicColors
import java.util.*
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
@ -35,27 +36,28 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
private val handler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
setDrawBehindSystemBars()
updateTheme()
hideStatusBar()
super.onCreate(savedInstanceState)
setImmersiveFullscreen()
setEdgeToEdgeOrImmersive()
registerSystemUiVisibility()
toggleScreenOn()
setLightNavigationAuto()
setLightNavigationBarAuto()
setLightStatusBarAuto(surfaceColor())
if (VersionUtils.hasQ()) {
window.decorView.isForceDarkAllowed = false
}
}
private fun updateTheme() {
setTheme(ThemeManager.getThemeResValue(this))
setDefaultNightMode(ThemeManager.getNightMode(this))
setTheme(ThemeManager.getThemeResValue())
setDefaultNightMode(ThemeManager.getNightMode())
// Apply dynamic colors to activity if enabled
if (PreferenceUtil.materialYou) {
DynamicColors.applyIfAvailable(
this,
com.google.android.material.R.style.ThemeOverlay_Material3_DynamicColors_DayNight
)
if (PreferenceUtil.isCustomFont) {
setTheme(R.style.FontThemeOverlay)
}
if (PreferenceUtil.circlePlayButton) {
setTheme(R.style.CircleFABOverlay)
}
}

View file

@ -14,12 +14,11 @@
*/
package code.name.monkey.retromusic.adapter
import android.os.SystemClock
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.os.bundleOf
@ -29,31 +28,22 @@ import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.fragments.home.HomeFragment
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
import code.name.monkey.retromusic.interfaces.IArtistClickListener
import code.name.monkey.retromusic.interfaces.IGenreClickListener
import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.material.card.MaterialCardView
class HomeAdapter(
private val activity: AppCompatActivity
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IArtistClickListener, IAlbumClickListener,
IGenreClickListener {
private var mLastClickTime: Long = 0
private var list = listOf<Home>()
override fun getItemViewType(position: Int): Int {
@ -65,17 +55,10 @@ class HomeAdapter(
LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
return when (viewType) {
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
GENRES -> GenreViewHolder(layout)
FAVOURITES -> PlaylistViewHolder(layout)
TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout)
else -> {
SuggestionsViewHolder(
LayoutInflater.from(activity).inflate(
R.layout.item_suggestions,
parent,
false
)
)
ArtistViewHolder(layout)
}
}
}
@ -127,10 +110,6 @@ class HomeAdapter(
)
}
}
SUGGESTIONS -> {
val viewHolder = holder as SuggestionsViewHolder
viewHolder.bindView(home)
}
FAVOURITES -> {
val viewHolder = holder as PlaylistViewHolder
viewHolder.bindView(home)
@ -142,12 +121,6 @@ class HomeAdapter(
)
}
}
GENRES -> {
val viewHolder = holder as GenreViewHolder
viewHolder.bind(home)
}
PLAYLISTS -> {
}
}
}
@ -155,6 +128,7 @@ class HomeAdapter(
return list.size
}
@SuppressLint("NotifyDataSetChanged")
fun swapData(sections: List<Home>) {
list = sections
notifyDataSetChanged()
@ -180,52 +154,6 @@ class HomeAdapter(
}
}
private inner class SuggestionsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val images = listOf(
R.id.image1,
R.id.image2,
R.id.image3,
R.id.image4,
R.id.image5,
R.id.image6,
R.id.image7,
R.id.image8
)
fun bindView(home: Home) {
val color = ThemeStore.accentColor(activity)
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()
}
}
}
itemView.findViewById<MaterialCardView>(R.id.card6).apply {
setCardBackgroundColor(ColorUtil.withAlpha(color, 0.12f))
}
images.forEachIndexed { index, id ->
itemView.findViewById<View>(id).setOnClickListener {
MusicPlayerRemote.playNext(home.arrayList[index] as Song)
if (!MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.playNextSong()
}
}
GlideApp.with(activity)
.asBitmap()
.songCoverOptions(home.arrayList[index] as Song)
.load(RetroGlideExtension.getSongModel(home.arrayList[index] as Song))
.into(itemView.findViewById(id))
}
}
}
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(home: Home) {
title.setText(home.titleRes)
@ -241,22 +169,6 @@ class HomeAdapter(
}
}
private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) {
fun bind(home: Home) {
arrow.hide()
title.setText(home.titleRes)
val genreAdapter = GenreAdapter(
activity,
home.arrayList as List<Genre>,
this@HomeAdapter
)
recyclerView.apply {
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.HORIZONTAL, false)
adapter = genreAdapter
}
}
}
open class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
val title: AppCompatTextView = itemView.findViewById(R.id.title)

View file

@ -14,6 +14,7 @@
*/
package code.name.monkey.retromusic.adapter
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -45,6 +46,7 @@ class SearchAdapter(
private var dataSet: List<Any>
) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() {
@SuppressLint("NotifyDataSetChanged")
fun swapDataSet(dataSet: List<Any>) {
this.dataSet = dataSet
notifyDataSetChanged()
@ -148,13 +150,13 @@ class SearchAdapter(
imageTextContainer?.isInvisible = true
if (itemViewType == SONG) {
imageTextContainer?.isGone = true
menu?.visibility = View.VISIBLE
menu?.isVisible = true
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
override val song: Song
get() = dataSet[layoutPosition] as Song
})
} else {
menu?.visibility = View.GONE
menu?.isVisible = false
}
when (itemViewType) {
@ -162,7 +164,7 @@ class SearchAdapter(
ARTIST -> setImageTransitionName(activity.getString(R.string.transition_artist_image))
else -> {
val container = itemView.findViewById<View>(R.id.imageContainer)
container?.visibility = View.GONE
container?.isVisible = false
}
}
}
@ -201,9 +203,8 @@ class SearchAdapter(
)
}
SONG -> {
val playList = mutableListOf<Song>()
playList.add(item as Song)
MusicPlayerRemote.openQueue(playList, 0, true)
MusicPlayerRemote.playNext(item as Song)
MusicPlayerRemote.playNextSong()
}
}
}

View file

@ -20,6 +20,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
@ -78,7 +79,7 @@ class SongFileAdapter(
if (holder.itemViewType == FILE) {
holder.text?.text = getFileText(file)
} else {
holder.text?.visibility = View.GONE
holder.text?.isVisible = false
}
}

View file

@ -20,6 +20,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
@ -174,7 +175,7 @@ open class AlbumAdapter(
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init {
menu?.visibility = View.GONE
menu?.isVisible = false
}
override fun onClick(v: View?) {

View file

@ -14,6 +14,7 @@
*/
package code.name.monkey.retromusic.adapter.artist
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.content.res.Resources
import android.view.LayoutInflater
@ -21,6 +22,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
@ -52,13 +54,17 @@ class ArtistAdapter(
activity, ICabHolder, R.menu.menu_media_selection
), PopupTextProvider {
var albumArtistsOnly = false
init {
this.setHasStableIds(true)
}
@SuppressLint("NotifyDataSetChanged")
fun swapDataSet(dataSet: List<Artist>) {
this.dataSet = dataSet
notifyDataSetChanged()
albumArtistsOnly = PreferenceUtil.albumArtistsOnly
}
override fun getItemId(position: Int): Long {
@ -86,7 +92,7 @@ class ArtistAdapter(
holder.title?.text = artist.name
holder.text?.hide()
val transitionName =
if (PreferenceUtil.albumArtistsOnly) artist.name else artist.id.toString()
if (albumArtistsOnly) artist.name else artist.id.toString()
if (holder.imageContainer != null) {
ViewCompat.setTransitionName(holder.imageContainer!!, transitionName)
} else {
@ -158,7 +164,7 @@ class ArtistAdapter(
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init {
menu?.visibility = View.GONE
menu?.isVisible = false
}
override fun onClick(v: View?) {
@ -168,7 +174,7 @@ class ArtistAdapter(
} else {
val artist = dataSet[layoutPosition]
image?.let {
if (PreferenceUtil.albumArtistsOnly && IAlbumArtistClickListener != null) {
if (albumArtistsOnly && IAlbumArtistClickListener != null) {
IAlbumArtistClickListener.onAlbumArtist(artist.name, imageContainer ?: it)
} else {
IArtistClickListener.onArtist(artist.id, imageContainer ?: it)

View file

@ -7,6 +7,7 @@ import android.view.MenuItem
import androidx.annotation.MenuRes
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.interfaces.ICabCallback
@ -31,7 +32,10 @@ abstract class AbsMultiSelectAdapter<V : RecyclerView.ViewHolder?, I>(
override fun onCabFinished(cab: AttachedCab): Boolean {
clearChecked()
activity.window.statusBarColor = Color.TRANSPARENT
activity.window.statusBarColor = when {
VersionUtils.hasMarshmallow() -> Color.TRANSPARENT
else -> Color.BLACK
}
return true
}

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.adapter.song
import android.view.MenuItem
import android.view.View
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.ThemeStore
@ -129,7 +130,7 @@ class OrderablePlaylistSongAdapter(
}
init {
dragView?.visibility = View.VISIBLE
dragView?.isVisible = true
}
override fun onClick(v: View?) {

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.adapter.song
import android.view.MenuItem
import android.view.View
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.GlideApp
@ -154,7 +155,7 @@ class PlayingQueueAdapter(
}
init {
dragView?.visibility = View.VISIBLE
dragView?.isVisible = true
}
override fun onSongMenuItemClick(item: MenuItem): Boolean {

View file

@ -15,6 +15,7 @@
package code.name.monkey.retromusic.adapter.song
import android.view.View
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.accentColor
@ -61,7 +62,7 @@ class ShuffleButtonSongAdapter(
super.onBindViewHolder(holder, position - 1)
val landscape = RetroUtil.isLandscape()
if ((PreferenceUtil.songGridSize > 2 && !landscape) || (PreferenceUtil.songGridSizeLand > 5 && landscape)) {
holder.menu?.visibility = View.GONE
holder.menu?.isVisible = false
}
}
}

View file

@ -0,0 +1,221 @@
/*
* 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.appwidgets
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
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
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.service.MusicService.ACTION_TOGGLE_PAUSE
import code.name.monkey.retromusic.service.MusicService.TOGGLE_FAVORITE
import code.name.monkey.retromusic.util.ImageUtil
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
class AppWidgetCircle : BaseAppWidget() {
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
/**
* Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running.
*/
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_circle)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_play_arrow,
secondaryColor
), 1f
)
)
linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView)
}
/**
* Update all active widget instances by pushing changes
*/
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_circle)
val isPlaying = service.isPlaying
val song = service.currentSong
// Set correct drawable for pause state
val playPauseRes =
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
playPauseRes,
MaterialValueHelper.getSecondaryTextColor(service, true)
), 1f
)
)
val isFavorite = runBlocking(Dispatchers.IO) {
return@runBlocking MusicUtil.repository.isSongFavorite(song.id)
}
val favoriteRes =
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
appWidgetView.setImageViewBitmap(
R.id.button_toggle_favorite, createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
favoriteRes,
MaterialValueHelper.getSecondaryTextColor(service, true)
), 1f
)
)
// Link actions buttons to intents
linkButtons(service, appWidgetView)
if (imageSize == 0) {
val p = RetroUtil.getScreenSize(service)
imageSize = p.x.coerceAtMost(p.y)
}
// Load the album cover async and push the update on completion
service.runOnUiThread {
if (target != null) {
Glide.with(service).clear(target)
}
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
.apply(
RequestOptions().transform(RoundedCorners(imageSize / 2))
)
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
val palette = resource.palette
update(
resource.bitmap, palette.getVibrantColor(
palette.getMutedColor(
MaterialValueHelper.getSecondaryTextColor(
service, true
)
)
)
)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
}
private fun update(bitmap: Bitmap?, color: Int) {
// Set correct drawable for pause state
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, ImageUtil.createBitmap(
ImageUtil.getTintedVectorDrawable(
service, playPauseRes, color
)
)
)
// Set favorite button drawables
appWidgetView.setImageViewBitmap(
R.id.button_toggle_favorite, ImageUtil.createBitmap(
ImageUtil.getTintedVectorDrawable(
service, favoriteRes, color
)
)
)
appWidgetView.setImageViewBitmap(R.id.image, bitmap)
pushUpdate(service, appWidgetIds, appWidgetView)
}
})
}
}
/**
* Link up various button actions using [PendingIntent].
*/
private fun linkButtons(context: Context, views: RemoteViews) {
val action = Intent(context, MainActivity::class.java)
.putExtra(
MainActivity.EXPAND_PANEL,
PreferenceUtil.isExpandPanel
)
val serviceName = ComponentName(context, MusicService::class.java)
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
var pendingIntent =
PendingIntent.getActivity(
context, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
views.setOnClickPendingIntent(R.id.image, pendingIntent)
// Favorite track
pendingIntent = buildPendingIntent(context, TOGGLE_FAVORITE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_favorite, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
}
companion object {
const val NAME = "app_widget_circle"
private var mInstance: AppWidgetCircle? = null
private var imageSize = 0
val instance: AppWidgetCircle
@Synchronized get() {
if (mInstance == null) {
mInstance = AppWidgetCircle()
}
return mInstance!!
}
}
}

View file

@ -208,7 +208,13 @@ class AppWidgetMD3 : BaseAppWidget() {
val image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = createRoundedBitmap(
image, imageSize, imageSize, cardRadius, cardRadius, cardRadius, cardRadius
image,
imageSize,
imageSize,
cardRadius,
cardRadius,
cardRadius,
cardRadius
)
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)

View file

@ -58,7 +58,7 @@ abstract class BaseAppWidget : AppWidgetProvider() {
*/
fun notifyChange(service: MusicService, what: String) {
if (hasInstances(service)) {
if (META_CHANGED == what || PLAY_STATE_CHANGED == what) {
if (META_CHANGED == what || PLAY_STATE_CHANGED == what || FAVORITE_STATE_CHANGED == what) {
performUpdate(service, null)
}
}

View file

@ -0,0 +1,58 @@
package code.name.monkey.retromusic.dialogs
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.DialogPlaybackSpeedBinding
import code.name.monkey.retromusic.extensions.accent
import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.materialDialog
import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.material.slider.Slider
class PlaybackSpeedDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val binding = DialogPlaybackSpeedBinding.inflate(layoutInflater)
binding.playbackSpeedSlider.accent()
binding.playbackPitchSlider.accent()
binding.playbackSpeedSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
binding.speedValue.text = "$value"
})
binding.playbackPitchSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
binding.pitchValue.text = "$value"
})
binding.playbackSpeedSlider.value = PreferenceUtil.playbackSpeed
binding.playbackPitchSlider.value = PreferenceUtil.playbackPitch
return materialDialog(R.string.playback_settings)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.save) { _, _ ->
updatePlaybackAndPitch(
binding.playbackSpeedSlider.value,
binding.playbackPitchSlider.value
)
}
.setNeutralButton(R.string.reset_action) {_, _ ->
updatePlaybackAndPitch(
1F,
1F
)
}
.setView(binding.root)
.create()
.colorButtons()
}
private fun updatePlaybackAndPitch(speed: Float, pitch: Float) {
PreferenceUtil.playbackSpeed = speed
PreferenceUtil.playbackPitch = pitch
}
companion object {
fun newInstance(): PlaybackSpeedDialog {
return PlaybackSpeedDialog()
}
}
}

View file

@ -14,7 +14,6 @@
*/
package code.name.monkey.retromusic.dialogs
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.Dialog
import android.app.PendingIntent
@ -23,7 +22,6 @@ import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.os.SystemClock
import android.view.LayoutInflater
import android.widget.CheckBox
import android.widget.SeekBar
import android.widget.TextView
@ -31,6 +29,7 @@ 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.databinding.DialogSleepTimerBinding
import code.name.monkey.retromusic.extensions.addAccentColor
import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.materialDialog
@ -49,31 +48,27 @@ class SleepTimerDialog : DialogFragment() {
private lateinit var timerUpdater: TimerUpdater
private lateinit var dialog: MaterialDialog
private lateinit var shouldFinishLastSong: CheckBox
private lateinit var seekBar: SeekBar
private lateinit var timerDisplay: TextView
@SuppressLint("InflateParams")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
timerUpdater = TimerUpdater()
val layout =
LayoutInflater.from(requireContext()).inflate(R.layout.dialog_sleep_timer, null)
shouldFinishLastSong = layout.findViewById(R.id.shouldFinishLastSong)
seekBar = layout.findViewById(R.id.seekBar)
timerDisplay = layout.findViewById(R.id.timerDisplay)
val binding = DialogSleepTimerBinding.inflate(layoutInflater)
shouldFinishLastSong = binding.shouldFinishLastSong
timerDisplay = binding.timerDisplay
val finishMusic = PreferenceUtil.isSleepTimerFinishMusic
shouldFinishLastSong.apply {
addAccentColor()
isChecked = finishMusic
}
seekBar.apply {
binding.seekBar.apply {
addAccentColor()
seekArcProgress = PreferenceUtil.lastSleepTimerValue
updateTimeDisplayTime()
seekBar.progress = seekArcProgress
progress = seekArcProgress
}
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
binding.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
if (i < 1) {
seekBar.progress = 1
@ -91,7 +86,7 @@ class SleepTimerDialog : DialogFragment() {
}
})
return materialDialog(R.string.action_sleep_timer)
.setView(layout)
.setView(binding.root)
.setPositiveButton(R.string.action_set) { _, _ ->
PreferenceUtil.isSleepTimerFinishMusic = shouldFinishLastSong.isChecked
val minutes = seekArcProgress
@ -170,7 +165,6 @@ class SleepTimerDialog : DialogFragment() {
) {
override fun onTick(millisUntilFinished: Long) {
seekBar.progress = millisUntilFinished.toInt()
}
override fun onFinish() {

View file

@ -4,14 +4,11 @@ import android.app.ActivityManager
import android.graphics.Color
import android.os.Build
import android.view.View
import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isGone
import androidx.core.view.*
import androidx.fragment.app.FragmentActivity
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
@ -25,6 +22,22 @@ fun AppCompatActivity.toggleScreenOn() {
}
}
fun AppCompatActivity.keepScreenOn(keepScreenOn: Boolean) {
if (keepScreenOn) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
fun AppCompatActivity.setEdgeToEdgeOrImmersive() {
if (PreferenceUtil.isFullScreenMode) {
setImmersiveFullscreen()
} else {
setDrawBehindSystemBars()
}
}
fun AppCompatActivity.setImmersiveFullscreen() {
if (PreferenceUtil.isFullScreenMode) {
WindowInsetsControllerCompat(window, window.decorView).apply {
@ -36,6 +49,14 @@ fun AppCompatActivity.setImmersiveFullscreen() {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { _, insets ->
if (insets.displayCutout != null) {
insets
} else {
// Consume insets if display doesn't have a Cutout
WindowInsetsCompat.CONSUMED
}
}
}
}
@ -57,19 +78,19 @@ private fun AppCompatActivity.hideStatusBar(fullscreen: Boolean) {
}
fun AppCompatActivity.setDrawBehindSystemBars() {
WindowCompat.setDecorFitsSystemWindows(window, false)
if (VersionUtils.hasOreo()) {
WindowCompat.setDecorFitsSystemWindows(window, false)
window.navigationBarColor = Color.TRANSPARENT
window.statusBarColor = Color.TRANSPARENT
if (VersionUtils.hasQ()) {
window.isNavigationBarContrastEnforced = false
}
window.navigationBarColor = Color.TRANSPARENT
window.statusBarColor = Color.TRANSPARENT
} else {
setNavigationBarColorPreOreo(surfaceColor())
if (VersionUtils.hasMarshmallow()) {
setStatusBarColor(Color.TRANSPARENT)
} else {
setStatusBarColor(surfaceColor())
setStatusBarColor(Color.BLACK)
}
}
}
@ -96,24 +117,49 @@ fun AppCompatActivity.setTaskDescriptionColorAuto() {
setTaskDescriptionColor(surfaceColor())
}
fun AppCompatActivity.setLightNavigationAuto() {
ATH.setLightNavigationBarAuto(this, surfaceColor())
@Suppress("Deprecation")
fun AppCompatActivity.setLightStatusBar(enabled: Boolean) {
if (VersionUtils.hasMarshmallow()) {
val decorView = window.decorView
val systemUiVisibility = decorView.systemUiVisibility
if (enabled) {
decorView.systemUiVisibility =
systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility =
systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
fun AppCompatActivity.setLightStatusBar(enabled: Boolean) {
ATH.setLightStatusBar(this, enabled)
fun AppCompatActivity.setLightStatusBarAuto() {
setLightStatusBar(surfaceColor().isColorLight)
}
fun AppCompatActivity.setLightStatusBarAuto(bgColor: Int) {
setLightStatusBar(ColorUtil.isColorLight(bgColor))
setLightStatusBar(bgColor.isColorLight)
}
@Suppress("Deprecation")
fun AppCompatActivity.setLightNavigationBar(enabled: Boolean) {
ATH.setLightNavigationBar(this, enabled)
if (VersionUtils.hasOreo()) {
val decorView = window.decorView
var systemUiVisibility = decorView.systemUiVisibility
systemUiVisibility = if (enabled) {
systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
} else {
systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
}
decorView.systemUiVisibility = systemUiVisibility
}
}
fun AppCompatActivity.setLightNavigationBarAuto() {
setLightNavigationBar(surfaceColor().isColorLight)
}
fun AppCompatActivity.setLightNavigationBarAuto(bgColor: Int) {
setLightNavigationBar(ColorUtil.isColorLight(bgColor))
setLightNavigationBar(bgColor.isColorLight)
}

View file

@ -43,6 +43,7 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.google.android.material.slider.Slider
import com.google.android.material.textfield.TextInputLayout
fun Int.ripAlpha(): Int {
@ -112,6 +113,22 @@ fun SeekBar.addAccentColor() {
thumbTintList = colorState
}
fun Slider.addAccentColor() {
if (materialYou) return
val accentColor = ThemeStore.accentColor(context)
trackActiveTintList = accentColor.colorStateList
trackInactiveTintList = ColorUtil.withAlpha(accentColor, 0.5F).colorStateList
thumbTintList = accentColor.colorStateList
}
fun Slider.accent() {
if (materialYou) return
val accentColor = context.accentColor()
thumbTintList = accentColor.colorStateList
trackActiveTintList = accentColor.colorStateList
trackInactiveTintList = ColorUtil.withAlpha(accentColor, 0.1F).colorStateList
}
fun Button.accentTextColor() {
if (materialYou) return
setTextColor(ThemeStore.accentColor(App.getContext()))
@ -248,15 +265,10 @@ fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
@ColorInt
fun Context.darkAccentColor(): Int {
val colorSurfaceVariant = if (surfaceColor().isColorLight) {
surfaceColor()
} else {
surfaceColor().lighterColor
}
return ColorUtils.blendARGB(
accentColor(),
colorSurfaceVariant,
if (surfaceColor().isColorLight) 0.96f else 0.975f
surfaceColor(),
if (surfaceColor().isColorLight) 0.9f else 0.92f
)
}
@ -269,6 +281,15 @@ fun Context.darkAccentColorVariant(): Int {
)
}
@ColorInt
fun Context.accentColorVariant(): Int {
return if (surfaceColor().isColorLight) {
accentColor().darkerColor
} else {
accentColor().lighterColor
}
}
inline val @receiver:ColorInt Int.isColorLight
get() = ColorUtil.isColorLight(this)
@ -277,3 +298,6 @@ inline val @receiver:ColorInt Int.lighterColor
inline val @receiver:ColorInt Int.darkerColor
get() = ColorUtil.darkenColor(this)
inline val Int.colorStateList : ColorStateList
get() = ColorStateList.valueOf(this)

View file

@ -17,6 +17,7 @@ package code.name.monkey.retromusic.extensions
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import code.name.monkey.retromusic.R
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
fun DialogFragment.materialDialog(title: Int): MaterialAlertDialogBuilder {

View file

@ -21,9 +21,9 @@ fun Fragment.createNewFile(
registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{ result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val outputStream: OutputStream? =
context?.contentResolver?.openOutputStream(result.data?.data!!)
write(outputStream, result.data?.data)
context?.contentResolver?.openOutputStream(result.data?.data!!)?.use { os->
write(os, result.data?.data)
}
}
}

View file

@ -0,0 +1,6 @@
package code.name.monkey.retromusic.extensions
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
val Song.uri get() = MusicUtil.getSongFileUri(songId = id)

View file

@ -20,11 +20,11 @@ import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.annotation.LayoutRes
import androidx.annotation.Px
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.view.*
@ -32,11 +32,8 @@ import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialdialogs.utils.MDUtil.updatePadding
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.shape.ShapeAppearanceModel
import dev.chrisbanes.insetter.applyInsetter
@Suppress("UNCHECKED_CAST")
fun <T : View> ViewGroup.inflate(@LayoutRes layout: Int): T {
@ -44,19 +41,17 @@ fun <T : View> ViewGroup.inflate(@LayoutRes layout: Int): T {
}
fun View.show() {
visibility = View.VISIBLE
isVisible = true
}
fun View.hide() {
visibility = View.GONE
isVisible = false
}
fun View.hidden() {
visibility = View.INVISIBLE
isInvisible = true
}
fun View.showOrHide(show: Boolean) = if (show) show() else hide()
fun EditText.appHandleColor(): EditText {
if (PreferenceUtil.materialYou) return this
TintHelper.colorHandles(this, ThemeStore.accentColor(context))
@ -126,40 +121,50 @@ fun View.focusAndShowKeyboard() {
}
}
fun ShapeableImageView.setCircleShape(boolean: Boolean) {
addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
val radius = width / 2f
shapeAppearanceModel = ShapeAppearanceModel().withCornerSize(radius)
}
}
/**
* This will draw our view above the navigation bar instead of behind it by adding margins.
*/
fun View.drawAboveSystemBars(onlyPortrait: Boolean = true) {
if (PreferenceUtil.isFullScreenMode) return
if (onlyPortrait && RetroUtil.isLandscape()) return
// Create a snapshot of the view's margin state
val initialMargin = recordInitialMarginForView(this)
ViewCompat.setOnApplyWindowInsetsListener(
(this)
) { _: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
// Apply the insets as a margin to the view.
updateLayoutParams<MarginLayoutParams> {
leftMargin = initialMargin.left + insets.left
bottomMargin = initialMargin.bottom + insets.bottom
rightMargin = initialMargin.right + insets.right
applyInsetter {
type(navigationBars = true) {
margin()
}
windowInsets
}
}
/**
* This will draw our view above the navigation bar instead of behind it by adding padding.
*/
fun View.drawAboveSystemBarsWithPadding(consume: Boolean = false) {
fun View.drawAboveSystemBarsWithPadding() {
if (PreferenceUtil.isFullScreenMode) return
applyInsetter {
type(navigationBars = true) {
padding()
}
}
}
fun View.drawNextToNavbar() {
if (PreferenceUtil.isFullScreenMode) return
applyInsetter {
type(statusBars = true, navigationBars = true) {
padding(horizontal = true)
}
}
}
fun View.updateMargin(
@Px left: Int = marginLeft,
@Px top: Int = marginTop,
@Px right: Int = marginRight,
@Px bottom: Int = marginBottom
) {
(layoutParams as ViewGroup.MarginLayoutParams).updateMargins(left, top, right, bottom)
}
fun View.applyBottomInsets() {
if (PreferenceUtil.isFullScreenMode) return
val initialPadding = recordInitialPaddingForView(this)
@ -168,11 +173,9 @@ fun View.drawAboveSystemBarsWithPadding(consume: Boolean = false) {
) { v: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
v.updatePadding(
left = initialPadding.left + insets.left,
bottom = initialPadding.bottom + insets.bottom,
right = initialPadding.right + insets.right
bottom = initialPadding.bottom + insets.bottom
)
if (consume) WindowInsetsCompat.CONSUMED else windowInsets
windowInsets
}
requestApplyInsetsWhenAttached()
}
@ -195,47 +198,6 @@ fun View.requestApplyInsetsWhenAttached() {
}
}
fun View.drawNextToNavbar() {
val initialPadding = recordInitialPaddingForView(this)
ViewCompat.setOnApplyWindowInsetsListener(
(this)
) { v: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
v.updatePadding(
left = initialPadding.left + insets.left,
right = initialPadding.right + insets.right
)
windowInsets
}
requestApplyInsetsWhenAttached()
}
fun View.addBottomInsets() {
// Create a snapshot of the view's margin state
val initialMargin = recordInitialMarginForView(this)
ViewCompat.setOnApplyWindowInsetsListener(
(this)
) { _: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
// Apply the insets as a margin to the view.
updateLayoutParams<MarginLayoutParams> {
bottomMargin = initialMargin.bottom + insets.bottom
}
windowInsets
}
}
data class InitialMargin(
val left: Int, val top: Int,
val right: Int, val bottom: Int
)
fun recordInitialMarginForView(view: View) = InitialMargin(
view.marginLeft, view.marginTop, view.marginRight, view.marginBottom
)
data class InitialPadding(
val left: Int, val top: Int,
val right: Int, val bottom: Int

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments
import android.animation.ValueAnimator
import android.widget.Toast
import androidx.core.animation.doOnEnd
import androidx.lifecycle.*
import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.db.*
@ -39,6 +40,7 @@ class LibraryViewModel(
private val _paletteColor = MutableLiveData<Int>()
private val home = MutableLiveData<List<Home>>()
private val suggestions = MutableLiveData<List<Song>>()
private val albums = MutableLiveData<List<Album>>()
private val songs = MutableLiveData<List<Song>>()
private val artists = MutableLiveData<List<Artist>>()
@ -55,6 +57,7 @@ class LibraryViewModel(
private fun loadLibraryContent() = viewModelScope.launch(IO) {
fetchHomeSections()
fetchSuggestions()
fetchSongs()
fetchAlbums()
fetchArtists()
@ -92,39 +95,34 @@ class LibraryViewModel(
return home
}
fun getSuggestions(): LiveData<List<Song>> {
return suggestions
}
fun getFabMargin(): LiveData<Int> {
return fabMargin
}
private fun fetchSongs() {
viewModelScope.launch(IO) {
private suspend fun fetchSongs() {
songs.postValue(repository.allSongs())
}
}
private fun fetchAlbums() {
viewModelScope.launch(IO) {
private suspend fun fetchAlbums() {
albums.postValue(repository.fetchAlbums())
}
}
private fun fetchArtists() {
private suspend fun fetchArtists() {
if (PreferenceUtil.albumArtistsOnly) {
viewModelScope.launch(IO) {
artists.postValue(repository.albumArtists())
}
} else {
viewModelScope.launch(IO) {
artists.postValue(repository.fetchArtists())
}
}
}
private fun fetchPlaylists() {
viewModelScope.launch(IO) {
private suspend fun fetchPlaylists() {
playlists.postValue(repository.fetchPlaylistWithSongs())
}
}
private fun fetchLegacyPlaylist() {
viewModelScope.launch(IO) {
@ -132,16 +130,16 @@ class LibraryViewModel(
}
}
private fun fetchGenres() {
viewModelScope.launch(IO) {
private suspend fun fetchGenres() {
genres.postValue(repository.fetchGenres())
}
}
fun fetchHomeSections() {
viewModelScope.launch(IO) {
private suspend fun fetchHomeSections() {
home.postValue(repository.homeSections())
}
private suspend fun fetchSuggestions() {
suggestions.postValue(repository.suggestions())
}
fun search(query: String?, filter: Filter) {
@ -151,7 +149,7 @@ class LibraryViewModel(
}
}
fun forceReload(reloadType: ReloadType) = viewModelScope.launch {
fun forceReload(reloadType: ReloadType) = viewModelScope.launch(IO) {
when (reloadType) {
Songs -> fetchSongs()
Albums -> fetchAlbums()
@ -159,6 +157,7 @@ class LibraryViewModel(
HomeSections -> fetchHomeSections()
Playlists -> fetchPlaylists()
Genres -> fetchGenres()
Suggestions -> fetchSuggestions()
}
}
@ -382,15 +381,16 @@ class LibraryViewModel(
fun setFabMargin(bottomMargin: Int) {
val currentValue = DensityUtil.dip2px(App.getContext(), 16F) +
bottomMargin
if (currentValue != fabMargin.value) {
ValueAnimator.ofInt(fabMargin.value!!, currentValue).apply {
addUpdateListener {
fabMargin.postValue(
it.animatedValue as Int
(it.animatedValue as Int)
)
}
start()
doOnEnd {
fabMargin.postValue(currentValue)
}
start()
}
}
}
@ -402,4 +402,5 @@ enum class ReloadType {
HomeSections,
Playlists,
Genres,
Suggestions
}

View file

@ -0,0 +1,61 @@
package code.name.monkey.retromusic.fragments
import android.annotation.SuppressLint
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import kotlinx.coroutines.*
/**
* @param activity, Activity
* @param next, if the button is next, if false then it's considered previous
*/
class MusicSeekSkipTouchListener(val activity: FragmentActivity, val next: Boolean) :
View.OnTouchListener {
var job: Job? = null
var counter = 0
var wasSeeking = false
private val gestureDetector = GestureDetector(activity, object :
GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent?): Boolean {
job = activity.lifecycleScope.launch(Dispatchers.Default) {
counter = 0
while (isActive) {
delay(500)
wasSeeking = true
var seekingDuration = MusicPlayerRemote.songProgressMillis
if (next) {
seekingDuration += 5000 * (counter.floorDiv(2) + 1)
} else {
seekingDuration -= 5000 * (counter.floorDiv(2) + 1)
}
MusicPlayerRemote.seekTo(seekingDuration)
counter += 1
}
}
return super.onDown(e)
}
})
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
val action = event?.actionMasked
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
job?.cancel()
if (!wasSeeking) {
if (next) {
MusicPlayerRemote.playNextSong()
} else {
MusicPlayerRemote.back()
}
}
wasSeeking = false
}
return gestureDetector.onTouchEvent(event)
}
}

View file

@ -107,7 +107,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
sharedElementEnterTransition = MaterialContainerTransform().apply {
drawingViewId = R.id.fragment_container
scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
setAllContainerColors(surfaceColor())
setPathMotion(MaterialArcMotion())
}
}

View file

@ -57,7 +57,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) {
remove()
mainActivity.finish()
requireActivity().onBackPressed()
}
}
}
@ -346,6 +346,13 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
libraryViewModel.forceReload(ReloadType.Albums)
}
override fun onPause() {
super.onPause()
if (cab.isActive()) {
cab.destroy()
}
}
private fun handleBackPress(): Boolean {
cab?.let {
if (it.isActive()) {

View file

@ -12,6 +12,7 @@ import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat
import androidx.core.view.ViewCompat
import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
@ -75,7 +76,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
sharedElementEnterTransition = MaterialContainerTransform().apply {
drawingViewId = R.id.fragment_container
scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
setAllContainerColors(surfaceColor())
}
}
@ -192,22 +193,24 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
if (lastFmArtist != null && lastFmArtist.artist != null && lastFmArtist.artist.bio != null) {
val bioContent = lastFmArtist.artist.bio.content
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
binding.fragmentArtistContent.biographyText.visibility = View.VISIBLE
binding.fragmentArtistContent.biographyTitle.visibility = View.VISIBLE
binding.fragmentArtistContent.run {
biographyText.isVisible = true
biographyTitle.isVisible = true
biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY)
binding.fragmentArtistContent.biographyText.text = biography
biographyText.text = biography
if (lastFmArtist.artist.stats.listeners.isNotEmpty()) {
binding.fragmentArtistContent.listeners.show()
binding.fragmentArtistContent.listenersLabel.show()
binding.fragmentArtistContent.scrobbles.show()
binding.fragmentArtistContent.scrobblesLabel.show()
binding.fragmentArtistContent.listeners.text =
listeners.show()
listenersLabel.show()
scrobbles.show()
scrobblesLabel.show()
listeners.text =
RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat())
binding.fragmentArtistContent.scrobbles.text =
scrobbles.text =
RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat())
}
}
}
}
// If the "lang" parameter is set and no biography is given, retry with default language
if (biography == null && lang != null) {

View file

@ -58,7 +58,7 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) {
remove()
mainActivity.finish()
requireActivity().onBackPressed()
}
}
}
@ -382,4 +382,11 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
super.onResume()
libraryViewModel.forceReload(ReloadType.Artists)
}
override fun onPause() {
super.onPause()
if (cab.isActive()) {
cab.destroy()
}
}
}

View file

@ -21,7 +21,7 @@ class BackupViewModel : ViewModel() {
val backupsLiveData: LiveData<List<File>> = backupsMutableLiveData
fun loadBackups(context: Context) {
BackupHelper.getBackupRoot(context).listFiles { _, name ->
BackupHelper.getBackupRoot().listFiles { _, name ->
return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION)
}?.toList()?.let {
backupsMutableLiveData.value = it

View file

@ -1,5 +1,6 @@
package code.name.monkey.retromusic.fragments.backup
import android.content.ContentResolver
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
@ -52,7 +53,7 @@ class RestoreActivity : AppCompatActivity() {
}
private fun updateTheme() {
AppCompatDelegate.setDefaultNightMode(ThemeManager.getNightMode(this))
AppCompatDelegate.setDefaultNightMode(ThemeManager.getNightMode())
// Apply dynamic colors to activity if enabled
if (PreferenceUtil.materialYou) {
@ -65,10 +66,10 @@ class RestoreActivity : AppCompatActivity() {
private fun getFileName(uri: Uri?): String? {
when (uri?.scheme) {
"file" -> {
ContentResolver.SCHEME_FILE -> {
return uri.lastPathSegment
}
"content" -> {
ContentResolver.SCHEME_CONTENT -> {
val proj = arrayOf(MediaStore.Files.FileColumns.DISPLAY_NAME)
contentResolver.query(
uri, proj, null, null, null

View file

@ -14,14 +14,27 @@
*/
package code.name.monkey.retromusic.fragments.base
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.core.view.isVisible
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.fragments.MusicSeekSkipTouchListener
import code.name.monkey.retromusic.fragments.other.VolumeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -36,22 +49,87 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
protected abstract fun hide()
protected abstract fun updateShuffleState()
protected abstract fun updateRepeatState()
protected abstract fun setUpProgressSlider()
abstract fun setColor(color: MediaNotificationProcessor)
fun showBounceAnimation(view: View) {
view.apply {
var lastPlaybackControlsColor: Int = 0
var lastDisabledPlaybackControlsColor: Int = 0
var isSeeking = false
private set
open val progressSlider: SeekBar? = null
abstract val shuffleButton: ImageButton
abstract val repeatButton: ImageButton
open val nextButton: ImageButton? = null
open val previousButton: ImageButton? = null
open val songTotalTime: TextView? = null
open val songCurrentProgress: TextView? = null
private var progressAnimator: ObjectAnimator? = null
override fun onUpdateProgressViews(progress: Int, total: Int) {
progressSlider?.max = total
if (isSeeking) {
progressSlider?.progress = progress
} else {
progressAnimator = ObjectAnimator.ofInt(progressSlider, "progress", progress).apply {
duration = SLIDER_ANIMATION_TIME
interpolator = LinearInterpolator()
start()
}
}
songTotalTime?.text = MusicUtil.getReadableDurationString(total.toLong())
songCurrentProgress?.text = MusicUtil.getReadableDurationString(progress.toLong())
}
private fun setUpProgressSlider() {
progressSlider?.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
onUpdateProgressViews(
progress,
MusicPlayerRemote.songDurationMillis
)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
isSeeking = true
progressViewUpdateHelper.stop()
progressAnimator?.cancel()
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
isSeeking = false
MusicPlayerRemote.seekTo(seekBar.progress)
progressViewUpdateHelper.start()
}
})
}
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
fun View.showBounceAnimation() {
clearAnimation()
scaleX = 0.9f
scaleY = 0.9f
visibility = View.VISIBLE
pivotX = (view.width / 2).toFloat()
pivotY = (view.height / 2).toFloat()
isVisible = true
pivotX = (width / 2).toFloat()
pivotY = (height / 2).toFloat()
animate().setDuration(200)
.setInterpolator(DecelerateInterpolator())
@ -67,13 +145,74 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
}
.start()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
hideVolumeIfAvailable()
}
override fun onStart() {
super.onStart()
setUpProgressSlider()
setUpPrevNext()
setUpShuffleButton()
setUpRepeatButton()
}
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() {
nextButton?.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), true))
previousButton?.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), false))
}
private fun setUpShuffleButton() {
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
private fun setUpRepeatButton() {
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
fun updatePrevNextColor() {
nextButton?.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
previousButton?.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
fun updateShuffleState() {
shuffleButton.setColorFilter(
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> lastPlaybackControlsColor
else -> lastDisabledPlaybackControlsColor
}, PorterDuff.Mode.SRC_IN
)
}
fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
repeatButton.setImageResource(R.drawable.ic_repeat)
repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
repeatButton.setImageResource(R.drawable.ic_repeat)
repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
repeatButton.setImageResource(R.drawable.ic_repeat_one)
repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
protected var volumeFragment: VolumeFragment? = null
private fun hideVolumeIfAvailable() {
@ -82,10 +221,20 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
.replace(R.id.volumeFragmentContainer, VolumeFragment()).commit()
childFragmentManager.executePendingTransactions()
volumeFragment =
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as VolumeFragment?
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as? VolumeFragment
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
companion object {
const val SLIDER_ANIMATION_TIME: Long = 400
}

View file

@ -32,13 +32,14 @@ import android.view.View
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.annotation.LayoutRes
import androidx.appcompat.graphics.drawable.DrawableWrapper
import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.navOptions
import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
import code.name.monkey.retromusic.R
@ -50,6 +51,7 @@ import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.dialogs.*
import code.name.monkey.retromusic.extensions.currentFragment
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.keepScreenOn
import code.name.monkey.retromusic.extensions.whichFragment
import code.name.monkey.retromusic.fragments.NowPlayingScreen
import code.name.monkey.retromusic.fragments.ReloadType
@ -78,9 +80,18 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
): Boolean {
val song = MusicPlayerRemote.currentSong
when (item.itemId) {
R.id.action_playback_speed -> {
PlaybackSpeedDialog.newInstance().show(childFragmentManager, "PLAYBACK_SETTINGS")
return true
}
R.id.action_toggle_lyrics -> {
PreferenceUtil.showLyrics = !item.isChecked
item.isChecked = !item.isChecked
PreferenceUtil.showLyrics = !PreferenceUtil.showLyrics
showLyricsIcon(item)
if (PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) {
mainActivity.keepScreenOn(true)
} else if (!PreferenceUtil.isScreenOnEnabled && !PreferenceUtil.showLyrics) {
mainActivity.keepScreenOn(false)
}
return true
}
R.id.action_go_to_lyrics -> {
@ -162,7 +173,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return true
}
R.id.action_sleep_timer -> {
SleepTimerDialog().show(parentFragmentManager, TAG)
SleepTimerDialog().show(parentFragmentManager, "SLEEP_TIMER")
return true
}
R.id.action_set_as_ringtone -> {
@ -193,6 +204,18 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return false
}
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(
requireContext(),
icon,
toolbarIconColor()
)
item.isChecked = PreferenceUtil.showLyrics
item.icon = drawable
}
abstract fun playerToolbar(): Toolbar?
abstract fun onShow()
@ -237,7 +260,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Main) {
val icon = if (animate) {
val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
@ -269,7 +292,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
if (PreferenceUtil.isFullScreenMode &&
view.findViewById<View>(R.id.status_bar) != null
) {
view.findViewById<View>(R.id.status_bar).visibility = View.GONE
view.findViewById<View>(R.id.status_bar).isVisible = false
}
playerAlbumCoverFragment = whichFragment(R.id.playerAlbumCoverFragment)
playerAlbumCoverFragment?.setCallbacks(this)
@ -287,9 +310,8 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
playerToolbar()?.menu?.removeItem(R.id.action_toggle_lyrics)
} else {
playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.apply {
fixCheckStateOnIcon()
isCheckable = true
isChecked = PreferenceUtil.showLyrics
showLyricsIcon(this)
}
}
requireView().setOnTouchListener(
@ -413,14 +435,3 @@ fun goToLyrics(activity: Activity) {
)
}
}
/** Fixes checked state being ignored by injecting checked state directly into drawable */
@SuppressLint("RestrictedApi")
class CheckDrawableWrapper(val menuItem: MenuItem) : DrawableWrapper(menuItem.icon) {
// inject checked state into drawable state set
override fun setState(stateSet: IntArray) = super.setState(
if (menuItem.isChecked) stateSet + android.R.attr.state_checked else stateSet
)
}
/** Wrap icon drawable with [CheckDrawableWrapper]. */
fun MenuItem.fixCheckStateOnIcon() = apply { icon = CheckDrawableWrapper(this) }

View file

@ -32,14 +32,11 @@ import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.dip
import code.name.monkey.retromusic.extensions.drawNextToNavbar
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.IScrollHelper
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ThemedFastScroller.create
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis
import me.zhanghai.android.fastscroll.FastScroller
import me.zhanghai.android.fastscroll.FastScrollerBuilder
@ -60,7 +57,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
view.doOnPreDraw { startPostponedEnterTransition() }
enterTransition = MaterialFadeThrough().addTarget(binding.recyclerView)
reenterTransition = MaterialFadeThrough().addTarget(binding.recyclerView)
mainActivity.setSupportActionBar(binding.toolbar)
mainActivity.setSupportActionBar(toolbar)
mainActivity.supportActionBar?.title = null
initLayoutManager()
initAdapter()
@ -99,25 +96,18 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
open fun onShuffleClicked() {
}
fun toolbar(): Toolbar {
return binding.toolbar
}
val toolbar: Toolbar get() = binding.appBarLayout.toolbar
private fun setupToolbar() {
binding.toolbar.setNavigationOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(requireView())
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
toolbar.setNavigationOnClickListener {
findNavController().navigate(
R.id.searchFragment,
R.id.action_search,
null,
navOptions
)
}
val appName = resources.getString(titleRes)
binding.appNameText.text = appName
binding.toolbarContainer.drawNextToNavbar()
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
binding.appBarLayout.title = appName
}
abstract val titleRes: Int
@ -155,17 +145,19 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
private fun checkIsEmpty() {
binding.emptyText.setText(emptyMessage)
binding.empty.visibility = if (adapter!!.itemCount == 0) View.VISIBLE else View.GONE
binding.empty.isVisible = adapter!!.itemCount == 0
}
private fun checkForPadding() {
val itemCount: Int = adapter?.itemCount ?: 0
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height_expanded))
binding.recyclerView.updatePadding(
bottom = if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
dip(R.dimen.mini_player_height_expanded)
} else {
binding.recyclerView.updatePadding(bottom = dip(R.dimen.bottom_nav_height))
dip(R.dimen.bottom_nav_height)
}
)
}
private fun initLayoutManager() {
@ -209,7 +201,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), binding.toolbar)
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), toolbar)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@ -217,9 +209,9 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
inflater.inflate(R.menu.menu_main, menu)
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
requireContext(),
binding.toolbar,
toolbar,
menu,
ATHToolbarActivity.getToolbarBackgroundColor(binding.toolbar)
ATHToolbarActivity.getToolbarBackgroundColor(toolbar)
)
}

View file

@ -24,9 +24,10 @@ import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.isVisible
import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
import androidx.navigation.Navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
@ -57,12 +58,10 @@ import code.name.monkey.retromusic.misc.WrappedAsyncTaskLoader
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.providers.BlacklistStore
import code.name.monkey.retromusic.util.*
import code.name.monkey.retromusic.util.DensityUtil.dip2px
import code.name.monkey.retromusic.util.PreferenceUtil.startDirectory
import code.name.monkey.retromusic.util.ThemedFastScroller.create
import code.name.monkey.retromusic.views.BreadCrumbLayout.Crumb
import code.name.monkey.retromusic.views.BreadCrumbLayout.SelectionCallback
import code.name.monkey.retromusic.views.ScrollingViewOnApplyWindowInsetsListener
import com.afollestad.materialcab.attached.AttachedCab
import com.afollestad.materialcab.attached.destroy
import com.afollestad.materialcab.attached.isActive
@ -71,7 +70,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.transition.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis
import java.io.*
import java.lang.ref.WeakReference
import java.util.*
@ -94,14 +92,10 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
}
}
private var storageItems = ArrayList<Storage>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
_binding = FragmentFolderBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
_binding = FragmentFolderBinding.bind(view)
mainActivity.addMusicServiceEventListener(libraryViewModel)
mainActivity.setSupportActionBar(binding.toolbar)
mainActivity.supportActionBar?.title = null
@ -118,7 +112,7 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
override fun handleOnBackPressed() {
if (!handleBackPress()) {
remove()
mainActivity.finish()
requireActivity().onBackPressed()
}
}
})
@ -128,10 +122,8 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
}
private fun setUpTitle() {
binding.toolbar.setNavigationOnClickListener { v: View? ->
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).setDuration(300)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).setDuration(300)
findNavController(v!!).navigate(R.id.searchFragment, null, navOptions)
binding.toolbar.setNavigationOnClickListener {
findNavController().navigate(R.id.action_search, null, navOptions)
}
binding.appNameText.text = resources.getString(R.string.folders)
}
@ -156,12 +148,8 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
override fun onPause() {
super.onPause()
saveScrollPosition()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
if (_binding != null) {
outState.putParcelable(CRUMBS, binding.breadCrumbs.stateWrapper)
if (cab.isActive()) {
cab.destroy()
}
}
@ -308,7 +296,7 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
openQueue(songs, startIndex, true)
} else {
Snackbar.make(
binding.root,
mainActivity.slidingPanel,
Html.fromHtml(
String.format(
getString(R.string.not_listed_in_media_store), file1.name
@ -381,6 +369,8 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(0, R.id.action_go_to_start_directory, 1, R.string.action_go_to_start_directory)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.add(0, R.id.action_settings, 2, R.string.action_settings)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
menu.removeItem(R.id.action_grid_size)
menu.removeItem(R.id.action_layout_type)
menu.removeItem(R.id.action_sort_order)
@ -416,6 +406,14 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
}
return true
}
R.id.action_settings -> {
findNavController().navigate(
R.id.settingsActivity,
null,
navOptions
)
return true
}
}
return super.onOptionsItemSelected(item)
}
@ -461,8 +459,7 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
private fun checkIsEmpty() {
if (_binding != null) {
binding.emptyEmoji.text = getEmojiByUnicode(0x1F631)
binding.empty.visibility =
if (adapter == null || adapter!!.itemCount == 0) View.VISIBLE else View.GONE
binding.empty.isVisible = adapter?.itemCount == 0
}
}

View file

@ -21,6 +21,7 @@ import android.view.MenuItem
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
@ -100,7 +101,7 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
private fun checkIsEmpty() {
checkForPadding()
binding.emptyEmoji.text = getEmojiByUnicode(0x1F631)
binding.empty.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
binding.empty.isVisible = songAdapter.itemCount == 0
}
private fun checkForPadding() {

View file

@ -48,7 +48,7 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
})
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
remove()
mainActivity.finish()
requireActivity().onBackPressed()
}
}

View file

@ -0,0 +1,23 @@
package code.name.monkey.retromusic.fragments.home
import code.name.monkey.retromusic.databinding.FragmentHomeBinding
class HomeBinding(
homeBinding: FragmentHomeBinding
) {
val root = homeBinding.root
val container = homeBinding.container
val contentContainer = homeBinding.contentContainer
val appBarLayout = homeBinding.appBarLayout
val toolbar = homeBinding.toolbar
val bannerImage = homeBinding.imageLayout.bannerImage
val userImage = homeBinding.imageLayout.userImage
val lastAdded = homeBinding.homeContent.absPlaylists.lastAdded
val topPlayed = homeBinding.homeContent.absPlaylists.topPlayed
val actionShuffle = homeBinding.homeContent.absPlaylists.actionShuffle
val history = homeBinding.homeContent.absPlaylists.history
val recyclerView = homeBinding.homeContent.recyclerView
val titleWelcome = homeBinding.imageLayout.titleWelcome
val appNameText = homeBinding.appNameText
val suggestions = homeBinding.homeContent.suggestions
}

View file

@ -1,31 +0,0 @@
package code.name.monkey.retromusic.fragments.home
import code.name.monkey.retromusic.databinding.FragmentBannerHomeBinding
import code.name.monkey.retromusic.databinding.FragmentHomeBinding
class HomeBindingAdapter(
homeBinding: FragmentHomeBinding?,
bannerHomeBinding: FragmentBannerHomeBinding?
) {
val root = homeBinding?.root ?: bannerHomeBinding?.root!!
val container = homeBinding?.container ?: bannerHomeBinding?.container!!
val contentContainer = homeBinding?.contentContainer ?: bannerHomeBinding?.contentContainer!!
val appBarLayout = homeBinding?.appBarLayout ?: bannerHomeBinding?.appBarLayout!!
val toolbar = homeBinding?.toolbar
?: bannerHomeBinding?.toolbar!!
val bannerImage = bannerHomeBinding?.bannerImage
val userImage = homeBinding?.userImage
?: bannerHomeBinding?.userImage!!
val lastAdded = homeBinding?.homeContent?.absPlaylists?.lastAdded
?: bannerHomeBinding?.homeContent?.absPlaylists?.lastAdded!!
val topPlayed = homeBinding?.homeContent?.absPlaylists?.topPlayed
?: bannerHomeBinding?.homeContent?.absPlaylists?.topPlayed!!
val actionShuffle = homeBinding?.homeContent?.absPlaylists?.actionShuffle
?: bannerHomeBinding?.homeContent?.absPlaylists?.actionShuffle!!
val history = homeBinding?.homeContent?.absPlaylists?.history
?: bannerHomeBinding?.homeContent?.absPlaylists?.history!!
val recyclerView = homeBinding?.homeContent?.recyclerView
?: bannerHomeBinding?.homeContent?.recyclerView!!
val titleWelcome = homeBinding?.titleWelcome ?: bannerHomeBinding?.titleWelcome!!
val appNameText = homeBinding?.appNameText ?: bannerHomeBinding?.appNameText!!
}

View file

@ -25,24 +25,29 @@ import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat
import androidx.core.view.doOnLayout
import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.adapter.HomeAdapter
import code.name.monkey.retromusic.databinding.FragmentBannerHomeBinding
import code.name.monkey.retromusic.databinding.FragmentHomeBinding
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.drawNextToNavbar
import code.name.monkey.retromusic.extensions.elevatedAccentColor
import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.IScrollHelper
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.shape.MaterialShapeDrawable
@ -50,15 +55,15 @@ import com.google.android.material.transition.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis
class HomeFragment :
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home),
IScrollHelper {
AbsMainActivityFragment(R.layout.fragment_home), IScrollHelper {
private var _binding: HomeBindingAdapter? = null
private var _binding: HomeBinding? = null
private val binding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = getBinding(PreferenceUtil.isHomeBanner, view)
val homeBinding = FragmentHomeBinding.bind(view)
_binding = HomeBinding(homeBinding)
mainActivity.setSupportActionBar(binding.toolbar)
mainActivity.supportActionBar?.title = null
setupListeners()
@ -75,6 +80,9 @@ class HomeFragment :
libraryViewModel.getHome().observe(viewLifecycleOwner, {
homeAdapter.swapData(it)
})
libraryViewModel.getSuggestions().observe(viewLifecycleOwner, {
loadSuggestions(it)
})
loadProfile()
setupTitle()
@ -86,7 +94,7 @@ class HomeFragment :
binding.toolbar.drawNextToNavbar()
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
remove()
mainActivity.finish()
requireActivity().onBackPressed()
}
view.doOnLayout {
adjustPlaylistButtons()
@ -150,24 +158,17 @@ class HomeFragment :
)
)
}
}
private fun getBinding(homeBanner: Boolean, view: View): HomeBindingAdapter {
return if (homeBanner) {
val homeBannerBinding = FragmentBannerHomeBinding.bind(view)
HomeBindingAdapter(null, homeBannerBinding)
} else {
val homeBinding = FragmentHomeBinding.bind(view)
HomeBindingAdapter(homeBinding, null)
// Reload suggestions
binding.suggestions.refreshButton.setOnClickListener {
libraryViewModel.forceReload(
ReloadType.Suggestions
)
}
}
private fun setupTitle() {
binding.toolbar.setNavigationOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(binding.root)
reenterTransition =
MaterialSharedAxis(MaterialSharedAxis.Z, false)
findNavController().navigate(R.id.searchFragment, null, navOptions)
findNavController().navigate(R.id.action_search, null, navOptions)
}
val hexColor = String.format("#%06X", 0xFFFFFF and accentColor())
val appName = HtmlCompat.fromHtml(
@ -234,6 +235,51 @@ class HomeFragment :
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
}
private fun loadSuggestions(songs: List<Song>) {
if (songs.isEmpty()) {
binding.suggestions.root.isVisible = false
return
}
val images = listOf(
binding.suggestions.image1,
binding.suggestions.image2,
binding.suggestions.image3,
binding.suggestions.image4,
binding.suggestions.image5,
binding.suggestions.image6,
binding.suggestions.image7,
binding.suggestions.image8
)
val color = ThemeStore.accentColor(requireContext())
binding.suggestions.message.apply {
setTextColor(color)
setOnClickListener {
it.isClickable = false
it.postDelayed({ it.isClickable = true }, 500)
MusicPlayerRemote.playNext(songs.subList(0, 8))
if (!MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.playNextSong()
}
}
}
binding.suggestions.card6.setCardBackgroundColor(ColorUtil.withAlpha(color, 0.12f))
images.forEachIndexed { index, imageView ->
imageView.setOnClickListener {
it.isClickable = false
it.postDelayed({ it.isClickable = true }, 500)
MusicPlayerRemote.playNext(songs[index])
if (!MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.playNextSong()
}
}
GlideApp.with(this)
.asBitmap()
.songCoverOptions(songs[index])
.load(RetroGlideExtension.getSongModel(songs[index]))
.into(imageView)
}
}
companion object {
const val TAG: String = "BannerHomeFragment"
@ -270,7 +316,7 @@ class HomeFragment :
override fun onResume() {
super.onResume()
libraryViewModel.fetchHomeSections()
libraryViewModel.forceReload(ReloadType.HomeSections)
}
override fun onDestroyView() {

View file

@ -54,7 +54,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
mainActivity.supportActionBar?.title = null
binding.toolbar.setNavigationOnClickListener {
findNavController().navigate(
R.id.searchFragment,
R.id.action_search,
null,
navOptions
)

View file

@ -12,8 +12,10 @@ import androidx.preference.PreferenceManager
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.SHOW_LYRICS
import code.name.monkey.retromusic.databinding.FragmentCoverLyricsBinding
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
@ -45,6 +47,14 @@ class CoverLyricsFragment : AbsMusicServiceFragment(R.layout.fragment_cover_lyri
if (PreferenceUtil.showLyrics) {
progressViewUpdateHelper?.start()
}
// Remove background on Fit theme
val nps = PreferenceUtil.nowPlayingScreen
if (nps == NowPlayingScreen.Fit || nps == NowPlayingScreen.Full) {
binding.root.background = null
}
binding.playerLyricsLine2.setOnClickListener {
goToLyrics(requireActivity())
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
@ -110,7 +120,7 @@ class CoverLyricsFragment : AbsMusicServiceFragment(R.layout.fragment_cover_lyri
if (lyrics !is AbsSynchronizedLyrics) return
val synchronizedLyrics = lyrics as AbsSynchronizedLyrics
lyricsLayout.visibility = View.VISIBLE
lyricsLayout.isVisible = true
lyricsLayout.alpha = 1f
val oldLine = lyricsLine2.text.toString()

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.other
import android.os.Bundle
import android.view.View
import androidx.activity.addCallback
import androidx.core.os.bundleOf
import androidx.core.view.doOnPreDraw
import androidx.navigation.fragment.FragmentNavigatorExtras
@ -32,17 +33,25 @@ import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.databinding.FragmentPlaylistDetailBinding
import code.name.monkey.retromusic.db.toSong
import code.name.monkey.retromusic.extensions.dipToPix
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
import code.name.monkey.retromusic.interfaces.IArtistClickListener
import code.name.monkey.retromusic.interfaces.ICabCallback
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialcab.attached.AttachedCab
import com.afollestad.materialcab.attached.destroy
import com.afollestad.materialcab.attached.isActive
import com.afollestad.materialcab.createCab
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialSharedAxis
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
IArtistClickListener, IAlbumClickListener {
IArtistClickListener, IAlbumClickListener, ICabHolder {
private val args by navArgs<DetailListFragmentArgs>()
private var _binding: FragmentPlaylistDetailBinding? = null
private val binding get() = _binding!!
@ -88,6 +97,12 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) {
remove()
findNavController().navigateUp()
}
}
}
private fun lastAddedSongs() {
@ -95,16 +110,16 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = ShuffleButtonSongAdapter(
requireActivity(),
mutableListOf(),
R.layout.item_list, null
R.layout.item_list, this
)
binding.recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager()
scheduleLayoutAnimation()
}
libraryViewModel.recentSongs().observe(viewLifecycleOwner, { songs ->
libraryViewModel.recentSongs().observe(viewLifecycleOwner) { songs ->
songAdapter.swapDataSet(songs)
})
}
}
private fun topPlayed() {
@ -112,15 +127,15 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = ShuffleButtonSongAdapter(
requireActivity(),
mutableListOf(),
R.layout.item_list, null
R.layout.item_list, this
)
binding.recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager()
}
libraryViewModel.playCountSongs().observe(viewLifecycleOwner, { songs ->
libraryViewModel.playCountSongs().observe(viewLifecycleOwner) { songs ->
songAdapter.swapDataSet(songs)
})
}
}
private fun loadHistory() {
@ -129,15 +144,15 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = ShuffleButtonSongAdapter(
requireActivity(),
mutableListOf(),
R.layout.item_list, null
R.layout.item_list, this
)
binding.recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager()
}
libraryViewModel.observableHistorySongs().observe(viewLifecycleOwner, {
libraryViewModel.observableHistorySongs().observe(viewLifecycleOwner) {
songAdapter.swapDataSet(it)
})
}
}
private fun loadFavorite() {
@ -145,50 +160,55 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = SongAdapter(
requireActivity(),
mutableListOf(),
R.layout.item_list, null
R.layout.item_list, this
)
binding.recyclerView.apply {
adapter = songAdapter
layoutManager = linearLayoutManager()
}
libraryViewModel.favorites().observe(viewLifecycleOwner, { songEntities ->
libraryViewModel.favorites().observe(viewLifecycleOwner) { songEntities ->
val songs = songEntities.map { songEntity -> songEntity.toSong() }
songAdapter.swapDataSet(songs)
})
}
}
private fun loadArtists(title: Int, type: Int) {
binding.toolbar.setTitle(title)
libraryViewModel.artists(type).observe(viewLifecycleOwner, { artists ->
val artistAdapter = artistAdapter(listOf())
binding.recyclerView.apply {
adapter = artistAdapter(artists)
adapter = artistAdapter
layoutManager = gridLayoutManager()
}
})
libraryViewModel.artists(type).observe(viewLifecycleOwner) { artists ->
artistAdapter.swapDataSet(artists)
}
}
private fun loadAlbums(title: Int, type: Int) {
binding.toolbar.setTitle(title)
libraryViewModel.albums(type).observe(viewLifecycleOwner, { albums ->
val albumAdapter = albumAdapter(listOf())
binding.recyclerView.apply {
adapter = albumAdapter(albums)
adapter = albumAdapter
layoutManager = gridLayoutManager()
}
})
libraryViewModel.albums(type).observe(viewLifecycleOwner) { albums ->
albumAdapter.swapDataSet(albums)
}
}
private fun artistAdapter(artists: List<Artist>): ArtistAdapter = ArtistAdapter(
requireActivity(),
artists,
R.layout.item_grid_circle,
null, this@DetailListFragment
this, this@DetailListFragment
)
private fun albumAdapter(albums: List<Album>): AlbumAdapter = AlbumAdapter(
requireActivity(),
albums,
R.layout.item_grid,
null, this@DetailListFragment
this, this@DetailListFragment
)
private fun linearLayoutManager(): LinearLayoutManager =
@ -228,4 +248,37 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
super.onDestroyView()
_binding = null
}
private var cab: AttachedCab? = null
private fun handleBackPress(): Boolean {
cab?.let {
if (it.isActive()) {
it.destroy()
return true
}
}
return false
}
override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab {
cab?.let {
println("Cab")
if (it.isActive()) {
it.destroy()
}
}
cab = createCab(R.id.toolbar_container) {
menu(menuRes)
closeDrawable(R.drawable.ic_close)
backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
slideDown()
onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
onSelection {
callback.onCabItemClicked(it)
}
onDestroy { callback.onCabFinished(it) }
}
return cab as AttachedCab
}
}

View file

@ -15,9 +15,16 @@
package code.name.monkey.retromusic.fragments.other
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.text.InputType
import android.view.*
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
@ -34,19 +41,16 @@ import code.name.monkey.retromusic.databinding.FragmentLyricsBinding
import code.name.monkey.retromusic.databinding.FragmentNormalLyricsBinding
import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.extensions.textColorSecondary
import code.name.monkey.retromusic.extensions.uri
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.lyrics.LrcView
import code.name.monkey.retromusic.model.AudioTagInfo
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialdialogs.LayoutMode
import code.name.monkey.retromusic.util.*
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.input.input
import com.google.android.material.color.MaterialColors
import com.google.android.material.tabs.TabLayoutMediator
@ -55,6 +59,7 @@ import kotlinx.coroutines.*
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey
import java.io.File
import java.io.FileOutputStream
import java.util.*
class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
@ -95,6 +100,43 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
return transform
}
private lateinit var normalLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var newSyncedLyricsLauncher: ActivityResultLauncher<Intent>
private lateinit var editSyncedLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var cacheFile: File
private var syncedLyrics: String = ""
private lateinit var syncedFileUri: Uri
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Normal lyrics launcher
normalLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
FileUtils.copyFileToUri(requireContext(), cacheFile, song.uri)
}
}
newSyncedLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
context?.contentResolver?.openOutputStream(result.data?.data!!)?.use {
it.write(syncedLyrics.toByteArray())
}
}
}
editSyncedLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
requireContext().contentResolver.openOutputStream(syncedFileUri)?.use { os ->
(os as FileOutputStream).channel.truncate(0)
os.write(syncedLyrics.toByteArray())
os.flush()
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
@ -105,15 +147,9 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
ViewCompat.setTransitionName(binding.container, "lyrics")
setupWakelock()
binding.tabLyrics.setBackgroundColor(surfaceColor())
binding.container.setBackgroundColor(surfaceColor())
setupViews()
setupToolbar()
updateTitleSong()
if (VersionUtils.hasR()) {
binding.editButton.isVisible = false
}
}
private fun setupViews() {
@ -144,7 +180,6 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
private fun setupToolbar() {
mainActivity.setSupportActionBar(binding.toolbar)
binding.toolbar.setBackgroundColor(surfaceColor())
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
binding.toolbar.setNavigationOnClickListener {
findNavController().navigateUp()
@ -204,7 +239,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
e.printStackTrace()
}
MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show {
MaterialDialog(requireContext()).show {
title(res = R.string.edit_normal_lyrics)
input(
hintRes = R.string.paste_lyrics_here,
@ -213,7 +248,24 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
) { _, input ->
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
syncedLyrics = input.toString()
GlobalScope.launch {
if (VersionUtils.hasR()) {
cacheFile = TagWriter.writeTagsToFilesR(
requireContext(), AudioTagInfo(
listOf(song.data), fieldKeyValueMap, null
)
)[0]
val pendingIntent =
MediaStore.createWriteRequest(
requireContext().contentResolver,
listOf(song.uri)
)
normalLyricsLauncher.launch(
IntentSenderRequest.Builder(pendingIntent).build()
)
} else {
TagWriter.writeTagsToFiles(
requireContext(), AudioTagInfo(
listOf(song.data), fieldKeyValueMap, null
@ -221,6 +273,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
)
}
}
}
positiveButton(res = R.string.save) {
(lyricsSectionsAdapter.fragments[1].first as NormalLyrics).loadNormalLyrics()
}
@ -233,15 +286,41 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
private fun editSyncedLyrics() {
val content: String = LyricUtil.getStringFromLrc(LyricUtil.getSyncedLyricsFile(song))
MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show {
MaterialDialog(requireContext()).show {
title(res = R.string.edit_synced_lyrics)
input(
hintRes = R.string.paste_timeframe_lyrics_here,
prefill = content,
inputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_CLASS_TEXT
) { _, input ->
if (VersionUtils.hasR()) {
syncedLyrics = input.toString()
val lrcFile = LyricUtil.getSyncedLyricsFile(song)
if (lrcFile?.exists() == true) {
syncedFileUri =
UriUtil.getUriFromPath(requireContext(), lrcFile.absolutePath)
val pendingIntent =
MediaStore.createWriteRequest(
requireContext().contentResolver,
listOf(syncedFileUri)
)
editSyncedLyricsLauncher.launch(
IntentSenderRequest.Builder(pendingIntent).build()
)
} else {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
intent.putExtra(
Intent.EXTRA_TITLE,
LyricUtil.getLrcOriginalPath(File(song.data).name)
)
newSyncedLyricsLauncher.launch(intent)
}
} else {
LyricUtil.writeLrc(song, input.toString())
}
}
positiveButton(res = R.string.save) {
(lyricsSectionsAdapter.fragments[0].first as SyncedLyrics).loadLRCLyrics()
}
@ -285,11 +364,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
} catch (e: Exception) {
e.printStackTrace()
}
if (lyrics.isNullOrEmpty()) {
binding.noLyricsFound.visibility = View.VISIBLE
} else {
binding.noLyricsFound.visibility = View.GONE
}
binding.noLyricsFound.isVisible = lyrics.isNullOrEmpty()
binding.normalLyrics.text = lyrics
}

View file

@ -51,7 +51,7 @@ class PlayingQueueRVFragment : AbsRecyclerViewFragment<PlayingQueueAdapter, Line
}
private fun setupToolbar() {
toolbar().apply {
toolbar.apply {
setNavigationOnClickListener {
findNavController().navigateUp()
}

View file

@ -26,6 +26,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.LYRICS_TYPE
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.SHOW_LYRICS
import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter
@ -43,6 +44,7 @@ import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.transform.CarousalPagerTransformer
import code.name.monkey.retromusic.transform.ParallaxPagerTransformer
import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.LyricsType
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlinx.coroutines.Dispatchers
@ -145,21 +147,6 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
setOnClickListener {
goToLyrics(requireActivity())
}
setOnFlingXListener { velocityX ->
when {
velocityX < 0 -> {
MusicPlayerRemote.playNextSong()
true
}
velocityX > 0 -> {
MusicPlayerRemote.playPreviousSong()
true
}
else -> {
false
}
}
}
}
}
@ -201,6 +188,8 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
showLyrics(false)
progressViewUpdateHelper?.stop()
}
} else if (key == LYRICS_TYPE) {
maybeInitLyrics()
}
}
@ -223,13 +212,22 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
}
private fun showLyrics(visible: Boolean) {
ObjectAnimator.ofFloat(lrcView, View.ALPHA, if (visible) 1F else 0F).apply {
binding.coverLyrics.isVisible = false
binding.lyricsView.isVisible = false
binding.viewPager.isVisible = true
val lyrics: View = if (PreferenceUtil.lyricsType == LyricsType.REPLACE_COVER) {
ObjectAnimator.ofFloat(viewPager, View.ALPHA, if (visible) 0F else 1F).start()
lrcView
} else {
ObjectAnimator.ofFloat(viewPager, View.ALPHA, 1F).start()
binding.coverLyrics
}
ObjectAnimator.ofFloat(lyrics, View.ALPHA, if (visible) 1F else 0F).apply {
doOnEnd {
_binding?.lyricsView?.isVisible = visible
lyrics.isVisible = visible
}
start()
}
ObjectAnimator.ofFloat(viewPager, View.ALPHA, if (visible) 0F else 1F).start()
}
private fun maybeInitLyrics() {
@ -237,7 +235,9 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
// Don't show lyrics container for below conditions
if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) {
showLyrics(true)
if (PreferenceUtil.lyricsType == LyricsType.REPLACE_COVER) {
progressViewUpdateHelper?.start()
}
} else {
showLyrics(false)
progressViewUpdateHelper?.stop()
@ -283,9 +283,9 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} else {
surfaceColor()
}
Color -> color.backgroundColor
Color ,Classic -> color.backgroundColor
Blur -> Color.BLACK
else -> color.backgroundColor
else -> surfaceColor()
}
)
}

View file

@ -15,13 +15,12 @@
package code.name.monkey.retromusic.fragments.player.adaptive
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
@ -32,10 +31,6 @@ import code.name.monkey.retromusic.databinding.FragmentAdaptivePlayerPlaybackCon
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -43,38 +38,35 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class AdaptivePlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_adaptive_player_playback_controls) {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
private var _binding: FragmentAdaptivePlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentAdaptivePlayerPlaybackControlsBinding.inflate(inflater, container, false)
return binding.root
}
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpMusicControllers()
_binding = FragmentAdaptivePlayerPlaybackControlsBinding.bind(view)
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.playPauseButton)
}
setUpPlayPauseFab()
}
private fun updateSong() {
@ -86,16 +78,6 @@ class AdaptivePlaybackControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper!!.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper!!.stop()
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateSong()
@ -163,7 +145,14 @@ class AdaptivePlaybackControlsFragment :
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
}
private fun updatePlayPauseDrawableState() {
@ -174,103 +163,9 @@ class AdaptivePlaybackControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
override fun show() {}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun show() {
}
override fun hide() {
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun hide() {}
override fun onDestroyView() {
super.onDestroyView()

View file

@ -16,13 +16,13 @@ package code.name.monkey.retromusic.fragments.player.blur
import android.animation.ObjectAnimator
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
@ -36,10 +36,6 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -48,28 +44,32 @@ class BlurPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_blur_player_playback_controls) {
private var _binding: FragmentBlurPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val progressSlider: SeekBar
get() = binding.progressSlider
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentBlurPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers()
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation()
}
setUpPlayPauseFab()
binding.title.isSelected = true
binding.text.isSelected = true
binding.title.setOnClickListener {
@ -93,16 +93,6 @@ class BlurPlaybackControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper!!.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper!!.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
@ -159,7 +149,14 @@ class BlurPlaybackControlsFragment :
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
}
private fun updatePlayPauseDrawableState() {
@ -170,72 +167,6 @@ class BlurPlaybackControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
@ -253,55 +184,6 @@ class BlurPlaybackControlsFragment :
}
}
private fun showBounceAnimation() {
binding.playPauseButton.apply {
clearAnimation()
scaleX = 0.9f
scaleY = 0.9f
visibility = View.VISIBLE
pivotX = (width / 2).toFloat()
pivotY = (height / 2).toFloat()
animate().setDuration(200)
.setInterpolator(DecelerateInterpolator())
.scaleX(1.1f)
.scaleY(1.1f)
.withEndAction {
animate().setDuration(200)
.setInterpolator(AccelerateInterpolator())
.scaleX(1f)
.scaleY(1f)
.alpha(1f).start()
}.start()
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View file

@ -96,11 +96,13 @@ class CardFragment : AbsPlayerFragment(R.layout.fragment_card_player) {
}
private fun setUpPlayerToolbar() {
binding.playerToolbar.inflateMenu(R.menu.menu_player)
binding.playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
binding.playerToolbar.setOnMenuItemClickListener(this)
binding.playerToolbar.apply {
inflateMenu(R.menu.menu_player)
setNavigationOnClickListener { requireActivity().onBackPressed() }
setOnMenuItemClickListener(this@CardFragment)
ToolbarContentTintHelper.colorizeToolbar(binding.playerToolbar, Color.WHITE, activity)
ToolbarContentTintHelper.colorizeToolbar(this, Color.WHITE, activity)
}
}
override fun onServiceConnected() {

View file

@ -19,6 +19,7 @@ import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
@ -35,10 +36,6 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -46,31 +43,29 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class CardPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_card_player_playback_controls) {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
private var _binding: FragmentCardPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val shuffleButton: ImageButton
get() = binding.mediaButton.shuffleButton
override val repeatButton: ImageButton
get() = binding.mediaButton.repeatButton
override val nextButton: ImageButton
get() = binding.mediaButton.nextButton
override val previousButton: ImageButton
get() = binding.mediaButton.previousButton
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentCardPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers()
binding.mediaButton.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.mediaButton.playPauseButton)
}
setUpPlayPauseFab()
binding.title.isSelected = true
binding.text.isSelected = true
binding.title.setOnClickListener {
@ -94,16 +89,6 @@ class CardPlaybackControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper!!.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper!!.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
@ -167,7 +152,14 @@ class CardPlaybackControlsFragment :
}
private fun setUpPlayPauseFab() {
binding.mediaButton.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.mediaButton.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
}
private fun updatePlayPauseDrawableState() {
@ -178,118 +170,15 @@ class CardPlaybackControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.mediaButton.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.mediaButton.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.mediaButton.nextButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
binding.mediaButton.previousButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
private fun setUpShuffleButton() {
binding.mediaButton.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.mediaButton.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.mediaButton.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.mediaButton.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.mediaButton.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.mediaButton.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.mediaButton.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.mediaButton.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.mediaButton.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.mediaButton.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
private fun updateProgressTextColor() {
val color = MaterialValueHelper.getPrimaryTextColor(context, false)
binding.songTotalTime.setTextColor(color)
binding.songCurrentProgress.setTextColor(color)
}
public override fun show() {
// Ignore
}
public override fun hide() {
// Ignore
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
public override fun show() {}
public override fun hide() {}
override fun onDestroyView() {
super.onDestroyView()

View file

@ -111,7 +111,8 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
setTitleTextColor(Color.WHITE)
setSubtitleTextColor(Color.WHITE)
ToolbarContentTintHelper.colorizeToolbar(binding.playerToolbar, Color.WHITE, activity)
}.setOnMenuItemClickListener(this)
setOnMenuItemClickListener(this@CardBlurFragment)
}
}
override fun onServiceConnected() {

View file

@ -16,11 +16,11 @@ package code.name.monkey.retromusic.fragments.player.cardblur
import android.animation.ObjectAnimator
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.TintHelper
@ -32,10 +32,7 @@ import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -43,23 +40,29 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class CardBlurPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_card_blur_player_playback_controls) {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var _binding: FragmentCardBlurPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val shuffleButton: ImageButton
get() = binding.mediaButton.shuffleButton
override val repeatButton: ImageButton
get() = binding.mediaButton.repeatButton
override val nextButton: ImageButton
get() = binding.mediaButton.nextButton
override val previousButton: ImageButton
get() = binding.mediaButton.previousButton
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentCardBlurPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers()
setUpPlayPauseFab()
binding.progressSlider.applyColor(Color.WHITE)
}
override fun setColor(color: MediaNotificationProcessor) {
@ -96,16 +99,6 @@ class CardBlurPlaybackControlsFragment :
binding.songInfo.setTextColor(color)
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
@ -139,78 +132,6 @@ class CardBlurPlaybackControlsFragment :
updateShuffleState()
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.mediaButton.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.mediaButton.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.mediaButton.nextButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
binding.mediaButton.previousButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
private fun setUpShuffleButton() {
binding.mediaButton.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.mediaButton.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.mediaButton.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.mediaButton.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.mediaButton.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.mediaButton.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.mediaButton.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.mediaButton.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.mediaButton.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.mediaButton.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
binding.mediaButton.playPauseButton.animate()
.scaleX(1f)
@ -228,33 +149,6 @@ class CardBlurPlaybackControlsFragment :
}
}
override fun setUpProgressSlider() {
binding.progressSlider.applyColor(Color.WHITE)
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View file

@ -15,6 +15,7 @@
package code.name.monkey.retromusic.fragments.player.circle
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
@ -22,9 +23,7 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.media.AudioManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.widget.SeekBar
@ -34,6 +33,7 @@ import code.name.monkey.appthemehelper.util.*
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.MusicSeekSkipTouchListener
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
@ -77,17 +77,10 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentCirclePlayerBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentCirclePlayerBinding.bind(view)
setupViews()
binding.title.isSelected = true
binding.title.setOnClickListener {
@ -132,10 +125,11 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
)
}
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
binding.nextButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), true))
binding.previousButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), false))
}
private fun updatePrevNextColor() {

View file

@ -39,6 +39,7 @@ import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.MusicSeekSkipTouchListener
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
@ -455,10 +456,21 @@ class ClassicPlayerFragment : AbsPlayerFragment(R.layout.fragment_classic_player
setUpProgressSlider()
}
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() {
updatePrevNextColor()
binding.playerControlsContainer.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.playerControlsContainer.previousButton.setOnClickListener { MusicPlayerRemote.back() }
binding.playerControlsContainer.nextButton.setOnTouchListener(
MusicSeekSkipTouchListener(
requireActivity(),
true
)
)
binding.playerControlsContainer.previousButton.setOnTouchListener(
MusicSeekSkipTouchListener(
requireActivity(),
false
)
)
}
private fun updatePrevNextColor() {

View file

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

View file

@ -17,16 +17,15 @@ package code.name.monkey.retromusic.fragments.player.color
import android.animation.Animator
import android.animation.ObjectAnimator
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewAnimationUtils
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
@ -39,52 +38,43 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlin.math.sqrt
class ColorPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_adaptive_player_playback_controls) {
AbsPlayerControlsFragment(R.layout.fragment_color_player_playback_controls) {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var _binding: FragmentColorPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentColorPlayerPlaybackControlsBinding.inflate(inflater, container, false)
return binding.root
}
override val repeatButton: ImageButton
get() = binding.repeatButton
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override val nextButton: ImageButton
get() = binding.nextButton
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpMusicControllers()
_binding = FragmentColorPlayerPlaybackControlsBinding.bind(view)
setUpPlayPauseFab()
binding.title.isSelected = true
binding.text.isSelected = true
binding.title.setOnClickListener {
@ -155,80 +145,23 @@ class ColorPlaybackControlsFragment :
private fun setUpPlayPauseFab() {
TintHelper.setTintAuto(binding.playPauseButton, Color.WHITE, true)
TintHelper.setTintAuto(binding.playPauseButton, Color.BLACK, false)
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
}
private fun updatePlayPauseDrawableState() {
binding.playPauseButton.setImageResource(
when {
MusicPlayerRemote.isPlaying -> binding.playPauseButton.setImageResource(R.drawable.ic_pause)
else -> binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow)
MusicPlayerRemote.isPlaying -> R.drawable.ic_pause
else -> R.drawable.ic_play_arrow
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
@ -248,32 +181,6 @@ class ColorPlaybackControlsFragment :
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
fun createRevealAnimator(view: View): Animator {
val location = IntArray(2)
binding.playPauseButton.getLocationOnScreen(location)

View file

@ -15,13 +15,13 @@
package code.name.monkey.retromusic.fragments.player.fit
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
@ -33,33 +33,41 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class FitPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_fit_playback_controls) {
private var _binding: FragmentFitPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentFitPlaybackControlsBinding.bind(view)
setUpMusicControllers()
setUpPlayPauseFab()
binding.title.isSelected = true
binding.text.isSelected = true
@ -70,15 +78,6 @@ class FitPlaybackControlsFragment :
binding.text.setOnClickListener {
goToArtist(requireActivity())
}
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation()
}
}
private fun updateSong() {
@ -93,16 +92,6 @@ class FitPlaybackControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
@ -163,7 +152,14 @@ class FitPlaybackControlsFragment :
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
}
private fun updatePlayPauseDrawableState() {
@ -174,72 +170,6 @@ class FitPlaybackControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
@ -257,55 +187,6 @@ class FitPlaybackControlsFragment :
}
}
private fun showBounceAnimation() {
binding.playPauseButton.apply {
clearAnimation()
scaleX = 0.9f
scaleY = 0.9f
visibility = View.VISIBLE
pivotX = (width / 2).toFloat()
pivotY = (height / 2).toFloat()
animate().setDuration(200)
.setInterpolator(DecelerateInterpolator())
.scaleX(1.1f)
.scaleY(1.1f)
.withEndAction {
animate().setDuration(200)
.setInterpolator(AccelerateInterpolator())
.scaleX(1f)
.scaleY(1f)
.alpha(1f).start()
}.start()
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View file

@ -15,11 +15,11 @@
package code.name.monkey.retromusic.fragments.player.flat
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
@ -33,11 +33,8 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -45,22 +42,28 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class FlatPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_flat_player_playback_controls), Callback {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var _binding: FragmentFlatPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton?
get() = null
override val previousButton: ImageButton?
get() = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentFlatPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers()
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.title.isSelected = true
binding.text.isSelected = true
binding.title.setOnClickListener {
@ -71,16 +74,6 @@ class FlatPlaybackControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
@ -157,10 +150,6 @@ class FlatPlaybackControlsFragment :
updatePlayPauseDrawableState()
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
}
private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) {
binding.playPauseButton.setImageResource(R.drawable.ic_pause)
@ -169,13 +158,6 @@ class FlatPlaybackControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun updateSong() {
val song = MusicPlayerRemote.currentSong
binding.title.text = song.title
@ -196,79 +178,6 @@ class FlatPlaybackControlsFragment :
updateShuffleState()
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View file

@ -18,7 +18,6 @@ import android.animation.ObjectAnimator
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
@ -26,10 +25,13 @@ import android.view.MenuItem
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding
import code.name.monkey.retromusic.db.PlaylistEntity
@ -44,9 +46,7 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
@ -66,17 +66,30 @@ class FullPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_full_player_controls),
PopupMenu.OnMenuItemClickListener {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private val libraryViewModel: LibraryViewModel by sharedViewModel()
private var _binding: FragmentFullPlayerControlsBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val progressSlider: SeekBar
get() = binding.progressSlider
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -94,16 +107,6 @@ class FullPlaybackControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
@ -190,10 +193,6 @@ class FullPlaybackControlsFragment :
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
setupFavourite()
setupMenu()
}
@ -213,43 +212,6 @@ class FullPlaybackControlsFragment :
return (parentFragment as FullPlayerFragment).onMenuItemClick(item!!)
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onRepeatModeChanged() {
updateRepeatState()
}
@ -258,53 +220,6 @@ class FullPlaybackControlsFragment :
updateShuffleState()
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
private fun setupFavourite() {
binding.songFavourite.setOnClickListener {
toggleFavorite(MusicPlayerRemote.currentSong)
@ -320,7 +235,7 @@ class FullPlaybackControlsFragment :
val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Dispatchers.Main) {
val icon = if (animate) {
val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border

View file

@ -28,16 +28,20 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.*
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.MusicSeekSkipTouchListener
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
@ -63,7 +67,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_player),
MusicProgressViewUpdateHelper.Callback, PopupMenu.OnMenuItemClickListener {
MusicProgressViewUpdateHelper.Callback,
View.OnLayoutChangeListener, PopupMenu.OnMenuItemClickListener {
private var lastColor: Int = 0
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
@ -126,9 +131,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
}
private fun setupPanel() {
binding.colorBackground.doOnLayout {
val panel = getQueuePanel()
panel.peekHeight = binding.container.height
if (!ViewCompat.isLaidOut(binding.colorBackground) || binding.colorBackground.isLayoutRequested) {
binding.colorBackground.addOnLayoutChangeListener(this)
return
}
}
@ -279,7 +284,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Dispatchers.Main) {
val icon = if (animate) {
val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
@ -382,10 +387,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
)
}
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() {
updatePrevNextColor()
binding.playbackControlsFragment.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.playbackControlsFragment.previousButton.setOnClickListener { MusicPlayerRemote.back() }
binding.playbackControlsFragment.nextButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), true))
binding.playbackControlsFragment.previousButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), false))
}
private fun updatePrevNextColor() {
@ -450,7 +456,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private fun updateLabel() {
(MusicPlayerRemote.playingQueue.size - 1).apply {
if (this == (MusicPlayerRemote.position)) {
binding.nextSong.text = "Last song"
binding.nextSong.text = context?.resources?.getString(R.string.last_song)
} else {
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
binding.nextSong.text = title
@ -458,6 +464,25 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
}
}
override fun onLayoutChange(
v: View?,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int
) {
val panel = getQueuePanel()
if (panel.state == STATE_COLLAPSED) {
panel.peekHeight = binding.container.height
} else if (panel.state == STATE_EXPANDED) {
panel.peekHeight = binding.container.height + navBarHeight
}
}
private fun setupRecyclerView() {
playingQueueAdapter = PlayingQueueAdapter(
requireActivity() as AppCompatActivity,

View file

@ -37,7 +37,6 @@ class HomePlayerFragment : AbsPlayerFragment(R.layout.fragment_home_player),
private var _binding: FragmentHomePlayerBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)

View file

@ -15,12 +15,13 @@
package code.name.monkey.retromusic.fragments.player.lockscreen
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
@ -32,10 +33,7 @@ import code.name.monkey.retromusic.extensions.ripAlpha
import code.name.monkey.retromusic.extensions.textColorSecondary
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -46,23 +44,34 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class LockScreenControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_lock_screen_playback_controls) {
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private var _binding: FragmentLockScreenPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentLockScreenPlaybackControlsBinding.bind(view)
setUpMusicControllers()
setUpPlayPauseFab()
binding.title.isSelected = true
}
@ -72,16 +81,6 @@ class LockScreenControlsFragment :
binding.text.text = String.format("%s - %s", song.artistName, song.albumName)
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper?.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper?.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
@ -157,72 +156,6 @@ class LockScreenControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpProgressSlider()
setUpShuffleButton()
setUpRepeatButton()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
@ -240,32 +173,6 @@ class LockScreenControlsFragment :
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View file

@ -19,7 +19,9 @@ import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
@ -30,10 +32,7 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -44,22 +43,35 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class MaterialControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_material_playback_controls) {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var _binding: FragmentMaterialPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentMaterialPlaybackControlsBinding.bind(view)
setUpMusicControllers()
setUpPlayPauseFab()
binding.title.isSelected = true
binding.text.isSelected = true
binding.title.setOnClickListener {
@ -83,16 +95,6 @@ class MaterialControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
@ -175,103 +177,9 @@ class MaterialControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
public override fun show() {}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_sharp)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_sharp)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one_sharp)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
}
public override fun hide() {
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
public override fun hide() {}
override fun onDestroyView() {
super.onDestroyView()

View file

@ -29,6 +29,7 @@ import code.name.monkey.retromusic.SNOWFALL
import code.name.monkey.retromusic.databinding.FragmentPlayerBinding
import code.name.monkey.retromusic.extensions.colorControlNormal
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.extensions.isColorLight
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
@ -167,7 +168,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player),
}
private fun startOrStopSnow(isSnowFalling: Boolean) {
if (isSnowFalling) {
if (isSnowFalling && !surfaceColor().isColorLight) {
binding.snowfallView.isVisible = true
binding.snowfallView.restartFalling()
} else {

View file

@ -14,13 +14,12 @@
*/
package code.name.monkey.retromusic.fragments.player.normal
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
@ -33,40 +32,41 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class PlayerPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_player_playback_controls) {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var _binding: FragmentPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val progressSlider: SeekBar
get() = binding.progressSlider
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers()
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.playPauseButton)
}
setUpPlayPauseFab()
binding.title.isSelected = true
binding.text.isSelected = true
binding.title.setOnClickListener {
@ -126,15 +126,6 @@ class PlayerPlaybackControlsFragment :
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
@ -161,7 +152,14 @@ class PlayerPlaybackControlsFragment :
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
}
private fun updatePlayPauseDrawableState() {
@ -172,72 +170,6 @@ class PlayerPlaybackControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
@ -255,32 +187,6 @@ class PlayerPlaybackControlsFragment :
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View file

@ -20,7 +20,9 @@ import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
@ -30,10 +32,7 @@ import code.name.monkey.retromusic.databinding.FragmentPeakControlPlayerBinding
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -44,26 +43,29 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class PeakPlayerControlFragment : AbsPlayerControlsFragment(R.layout.fragment_peak_control_player) {
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private var _binding: FragmentPeakControlPlayerBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(
view: View,
@ -71,14 +73,12 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment(R.layout.fragment_pe
) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentPeakControlPlayerBinding.bind(view)
setUpMusicControllers()
setUpPlayPauseFab()
}
override fun show() {
}
override fun show() {}
override fun hide() {
}
override fun hide() {}
override fun setColor(color: MediaNotificationProcessor) {
val controlsColor =
@ -116,108 +116,12 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment(R.layout.fragment_pe
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener {
MusicPlayerRemote.toggleShuffleMode()
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener {
MusicPlayerRemote.cycleRepeatMode()
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
private fun setUpPlayPauseFab() {
TintHelper.setTintAuto(binding.playPauseButton, Color.WHITE, true)
TintHelper.setTintAuto(binding.playPauseButton, Color.BLACK, false)
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
override fun onPlayStateChanged() {
super.onPlayStateChanged()
updatePlayPauseDrawableState()

View file

@ -15,13 +15,13 @@
package code.name.monkey.retromusic.fragments.player.plain
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
@ -35,10 +35,6 @@ import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -50,12 +46,30 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class PlainPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_plain_controls_fragment) {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var _binding: FragmentPlainControlsFragmentBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onPlayStateChanged() {
updatePlayPauseDrawableState()
}
@ -89,59 +103,23 @@ class PlainPlaybackControlsFragment :
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentPlainControlsFragmentBinding.bind(view)
setUpMusicControllers()
setUpPlayPauseFab()
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation()
it.showBounceAnimation()
}
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
override fun setColor(color: MediaNotificationProcessor) {
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
if (ColorUtil.isColorLight(colorBg)) {
@ -179,53 +157,6 @@ class PlainPlaybackControlsFragment :
updatePrevNextColor()
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
@ -243,29 +174,6 @@ class PlainPlaybackControlsFragment :
}
}
private fun showBounceAnimation() {
binding.playPauseButton.apply {
clearAnimation()
scaleX = 0.9f
scaleY = 0.9f
visibility = View.VISIBLE
pivotX = (width / 2).toFloat()
pivotY = (height / 2).toFloat()
animate().setDuration(200)
.setInterpolator(DecelerateInterpolator())
.scaleX(1.1f)
.scaleY(1.1f)
.withEndAction {
animate().setDuration(200)
.setInterpolator(AccelerateInterpolator())
.scaleX(1f)
.scaleY(1f)
.alpha(1f).start()
}.start()
}
}
private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) {
binding.playPauseButton.setImageResource(R.drawable.ic_pause)
@ -274,32 +182,6 @@ class PlainPlaybackControlsFragment :
}
}
override fun setUpProgressSlider() {
binding.progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(
MusicPlayerRemote.songProgressMillis,
MusicPlayerRemote.songDurationMillis
)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View file

@ -14,10 +14,10 @@
*/
package code.name.monkey.retromusic.fragments.player.simple
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.ImageButton
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
@ -32,9 +32,6 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -49,10 +46,17 @@ class SimplePlaybackControlsFragment :
private var _binding: FragmentSimpleControlsFragmentBinding? = null
private val binding get() = _binding!!
override val shuffleButton: ImageButton
get() = binding.shuffleButton
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override fun onPlayStateChanged() {
updatePlayPauseDrawableState()
@ -73,37 +77,15 @@ class SimplePlaybackControlsFragment :
updateSong()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentSimpleControlsFragmentBinding.bind(view)
setUpMusicControllers()
setUpPlayPauseFab()
binding.title.isSelected = true
binding.text.setOnClickListener {
goToArtist(requireActivity())
}
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.playPauseButton)
}
binding.title.setOnClickListener {
goToAlbum(requireActivity())
}
@ -112,72 +94,6 @@ class SimplePlaybackControlsFragment :
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() {
updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
binding.nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
binding.previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private fun setUpShuffleButton() {
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
private fun setUpRepeatButton() {
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
private fun updateSong() {
val song = MusicPlayerRemote.currentSong
binding.title.text = song.title
@ -213,9 +129,6 @@ class SimplePlaybackControlsFragment :
}
}
override fun setUpProgressSlider() {
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.songCurrentProgress.text = String.format(
"%s / %s",
@ -263,7 +176,14 @@ class SimplePlaybackControlsFragment :
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
}
private fun updatePlayPauseDrawableState() {

View file

@ -14,15 +14,13 @@
*/
package code.name.monkey.retromusic.fragments.player.tiny
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.View
import android.widget.ImageButton
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentTinyControlsFragmentBinding
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class TinyPlaybackControlsFragment :
@ -30,14 +28,15 @@ class TinyPlaybackControlsFragment :
private var _binding: FragmentTinyControlsFragmentBinding? = null
private val binding get() = _binding!!
override fun show() {
}
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override fun hide() {
}
override val repeatButton: ImageButton
get() = binding.repeatButton
override fun setUpProgressSlider() {
}
override fun show() {}
override fun hide() {}
override fun setColor(color: MediaNotificationProcessor) {
lastPlaybackControlsColor = color.secondaryTextColor
@ -50,66 +49,9 @@ class TinyPlaybackControlsFragment :
override fun onUpdateProgressViews(progress: Int, total: Int) {
}
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentTinyControlsFragmentBinding.bind(view)
setUpMusicControllers()
}
private fun setUpMusicControllers() {
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpShuffleButton() {
binding.playerShuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
private fun setUpRepeatButton() {
binding.playerRepeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
override fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.playerShuffleButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
else -> binding.playerShuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
override fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat)
binding.playerRepeatButton.setColorFilter(
lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_ALL -> {
binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat)
binding.playerRepeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
MusicService.REPEAT_MODE_THIS -> {
binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.playerRepeatButton.setColorFilter(
lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN
)
}
}
}
override fun onServiceConnected() {

View file

@ -59,11 +59,9 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player),
return binding.playerToolbar
}
override fun onShow() {
}
override fun onShow() {}
override fun onHide() {
}
override fun onHide() {}
override fun onBackPressed(): Boolean {
return false

View file

@ -20,6 +20,7 @@ import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.toSongs
import code.name.monkey.retromusic.extensions.dip
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.extensions.updateMargin
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
@ -149,8 +150,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
}
private fun showEmptyView() {
binding.empty.visibility = View.VISIBLE
binding.emptyText.visibility = View.VISIBLE
binding.empty.isVisible = true
binding.emptyText.isVisible = true
}
fun songs(songs: List<Song>) {

View file

@ -48,7 +48,7 @@ class PlaylistsFragment :
})
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
remove()
mainActivity.finish()
requireActivity().onBackPressed()
}
}

View file

@ -130,7 +130,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
}
private fun updateCurrentSong() {
binding.toolbar.subtitle = getUpNextAndQueueTime()
binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
}
override fun onPlayingMetaChanged() {
@ -140,7 +140,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
private fun updateQueuePosition() {
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
resetToCurrentPosition()
binding.toolbar.subtitle = getUpNextAndQueueTime()
binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
}
private fun updateQueue() {
@ -179,7 +179,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
}
private fun setupToolbar() {
binding.toolbar.subtitle = getUpNextAndQueueTime()
binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor(
@ -190,10 +190,13 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
binding.clearQueue.setTextColor(this)
binding.clearQueue.iconTint = this
}
binding.toolbar.apply {
binding.appBarLayout.pinWhenScrolled()
binding.appBarLayout.toolbar.apply {
setNavigationOnClickListener {
findNavController().navigateUp()
}
setTitle(R.string.now_playing_queue)
setTitleTextAppearance(context, R.style.ToolbarTextAppearanceNormal)
setNavigationIcon(R.drawable.ic_keyboard_backspace_black)
ToolbarContentTintHelper.colorBackButton(this)
}

View file

@ -42,7 +42,7 @@ import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.transition.MaterialSharedAxis
import com.google.android.material.transition.MaterialFadeThrough
import net.yslibrary.android.keyboardvisibilityevent.KeyboardVisibilityEvent
import java.util.*
import kotlin.collections.ArrayList
@ -63,8 +63,8 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(view)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
enterTransition = MaterialFadeThrough().addTarget(view)
reenterTransition = MaterialFadeThrough().addTarget(view)
_binding = FragmentSearchBinding.bind(view)
mainActivity.setSupportActionBar(binding.toolbar)
libraryViewModel.clearSearchResult()
@ -147,7 +147,7 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
super.onChanged()
binding.empty.isVisible = searchAdapter.itemCount < 1
val height = dipToPix(52f)
binding.recyclerView.setPadding(0, 0, 0, height.toInt())
binding.recyclerView.updatePadding(bottom = height.toInt())
}
})
binding.recyclerView.apply {
@ -226,11 +226,6 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
hideKeyboard(view)
}
override fun onResume() {
super.onResume()
mainActivity.setBottomNavVisibility(false)
}
private fun hideKeyboard(view: View?) {
if (view != null) {
val imm: InputMethodManager =

View file

@ -20,18 +20,14 @@ import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat
import code.name.monkey.retromusic.activities.OnThemeChangedListener
import code.name.monkey.retromusic.extensions.rootView
import code.name.monkey.retromusic.extensions.safeGetBottomInsets
import code.name.monkey.retromusic.preferences.*
import code.name.monkey.retromusic.util.NavigationUtil
import dev.chrisbanes.insetter.applyInsetter
/**
* @author Hemanth S (h4h13).
@ -74,19 +70,10 @@ abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() {
listView.overScrollMode = View.OVER_SCROLL_NEVER
}
// CollapsingToolbarLayout consumes insets and insets are not passed to child views
// So we get insets from decor view
// https://github.com/material-components/material-components-android/issues/1310
ViewCompat.setOnApplyWindowInsetsListener(
requireActivity().rootView
) { _, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
listView.updatePadding(
left = insets.left,
bottom = insets.bottom,
right = insets.right,
)
windowInsets
listView.applyInsetter {
type(navigationBars = true) {
padding()
}
}
invalidateSettings()
}

View file

@ -19,17 +19,14 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentMainSettingsBinding
import code.name.monkey.retromusic.extensions.drawAboveSystemBarsWithPadding
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.rootView
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.util.NavigationUtil
@ -40,17 +37,20 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
override fun onClick(view: View) {
findNavController().navigate(
when (view.id) {
R.id.generalSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_themeSettingsFragment)
R.id.audioSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_audioSettings)
R.id.personalizeSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_personalizeSettingsFragment)
R.id.imageSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_imageSettingFragment)
R.id.notificationSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_notificationSettingsFragment)
R.id.otherSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_otherSettingsFragment)
R.id.aboutSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_aboutActivity)
R.id.nowPlayingSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_nowPlayingSettingsFragment)
R.id.backup_restore_settings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_backupFragment)
R.id.generalSettings -> R.id.action_mainSettingsFragment_to_themeSettingsFragment
R.id.audioSettings -> R.id.action_mainSettingsFragment_to_audioSettings
R.id.personalizeSettings -> R.id.action_mainSettingsFragment_to_personalizeSettingsFragment
R.id.imageSettings -> R.id.action_mainSettingsFragment_to_imageSettingFragment
R.id.notificationSettings -> R.id.action_mainSettingsFragment_to_notificationSettingsFragment
R.id.otherSettings -> R.id.action_mainSettingsFragment_to_otherSettingsFragment
R.id.aboutSettings -> R.id.action_mainSettingsFragment_to_aboutActivity
R.id.nowPlayingSettings -> R.id.action_mainSettingsFragment_to_nowPlayingSettingsFragment
R.id.backup_restore_settings -> R.id.action_mainSettingsFragment_to_backupFragment
else -> R.id.action_mainSettingsFragment_to_themeSettingsFragment
}
)
}
override fun onCreateView(
@ -89,17 +89,7 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
binding.diamondIcon.imageTintList = ColorStateList.valueOf(it)
}
ViewCompat.setOnApplyWindowInsetsListener(
requireActivity().rootView
) { _, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
_binding?.container?.updatePadding(
left = insets.left,
bottom = insets.bottom,
right = insets.right,
)
windowInsets
}
binding.container.drawAboveSystemBarsWithPadding()
}
override fun onDestroyView() {

View file

@ -14,6 +14,7 @@
*/
package code.name.monkey.retromusic.fragments.settings
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import androidx.preference.Preference
@ -37,6 +38,7 @@ import com.google.android.material.color.DynamicColors
*/
class ThemeSettingsFragment : AbsSettingsFragment() {
@SuppressLint("CheckResult")
override fun invalidateSettings() {
val generalTheme: Preference? = findPreference(GENERAL_THEME)
generalTheme?.let {
@ -121,6 +123,16 @@ class ThemeSettingsFragment : AbsSettingsFragment() {
restartActivity()
true
}
val wallpaperAccent: ATESwitchPreference? = findPreference(WALLPAPER_ACCENT)
wallpaperAccent?.setOnPreferenceChangeListener { _, _ ->
restartActivity()
true
}
val customFont: ATESwitchPreference? = findPreference(CUSTOM_FONT)
customFont?.setOnPreferenceChangeListener { _, _ ->
restartActivity()
true
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -51,12 +51,11 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) {
remove()
mainActivity.finish()
requireActivity().onBackPressed()
}
}
}
override val titleRes: Int
get() = R.string.songs
@ -346,6 +345,13 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
libraryViewModel.forceReload(ReloadType.Songs)
}
override fun onPause() {
super.onPause()
if (cab.isActive()) {
cab.destroy()
}
}
companion object {
@JvmField
var TAG: String = SongsFragment::class.java.simpleName

View file

@ -28,7 +28,7 @@ object BackupHelper : KoinComponent {
suspend fun createBackup(context: Context, name: String) {
val backupFile =
File(getBackupRoot(context), name + APPEND_EXTENSION)
File(getBackupRoot(), name + APPEND_EXTENSION)
if (backupFile.parentFile?.exists() != true) {
backupFile.parentFile?.mkdirs()
}
@ -257,9 +257,9 @@ object BackupHelper : KoinComponent {
}
}
fun getBackupRoot(context: Context): File {
fun getBackupRoot(): File {
return File(
context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS),
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
"RetroMusic/Backups"
)
}

View file

@ -15,12 +15,10 @@
package code.name.monkey.retromusic.helper
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Activity
import android.content.*
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.IBinder
import android.provider.DocumentsContract
@ -440,12 +438,14 @@ object MusicPlayerRemote : KoinComponent {
} else if (uri.authority == "media") {
songId = uri.lastPathSegment
}
if (songId != null) {
songs = songRepository.songs(songId)
songs = if (songId != null) {
songRepository.songs(songId)
} else {
songRepository.songsIgnoreBlacklist(uri)
}
}
}
if (songs == null) {
if (songs == null || songs.isEmpty()) {
var songFile: File? = null
if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") {
songFile = File(
@ -462,7 +462,7 @@ object MusicPlayerRemote : KoinComponent {
songFile = File(uri.path!!)
}
if (songFile != null) {
songs = songRepository.songsByFilePath(songFile.absolutePath)
songs = songRepository.songsByFilePath(songFile.absolutePath, true)
}
}
if (songs != null && songs.isNotEmpty()) {
@ -474,7 +474,6 @@ object MusicPlayerRemote : KoinComponent {
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private fun getSongIdFromMediaProvider(uri: Uri): String {
return DocumentsContract.getDocumentId(uri).split(":".toRegex())
.dropLastWhile { it.isEmpty() }.toTypedArray()[1]

View file

@ -0,0 +1,52 @@
package code.name.monkey.retromusic.helper
import android.app.WallpaperManager
import android.content.Context
import android.os.Handler
import android.os.Looper
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.util.PreferenceUtil
class WallpaperAccentManager(val context: Context) {
private val onColorsChangedListener by lazy {
WallpaperManager.OnColorsChangedListener { _, _ ->
updateColors()
}
}
fun init() {
if (VersionUtils.hasOreoMR1()) {
with(WallpaperManager.getInstance(context)) {
updateColors()
if (PreferenceUtil.wallpaperAccent) {
addOnColorsChangedListener(
onColorsChangedListener,
Handler(Looper.getMainLooper())
)
}
}
}
}
fun release() {
if (VersionUtils.hasOreoMR1()) {
WallpaperManager.getInstance(context)
.removeOnColorsChangedListener(onColorsChangedListener)
}
}
private fun updateColors() {
if (VersionUtils.hasOreoMR1()) {
val colors = WallpaperManager.getInstance(context)
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
if (colors != null) {
val primaryColor = colors.primaryColor.toArgb()
if (primaryColor != ThemeStore.wallpaperColor(context)) {
ThemeStore.editTheme(context).wallpaperColor(primaryColor).commit()
}
}
}
}
}

View file

@ -67,7 +67,6 @@ class CoverLrcView @JvmOverloads constructor(
private var mDefaultLabel: String? = null
private var mLrcPadding = 0f
private var mOnPlayClickListener: OnPlayClickListener? = null
private var mOnFlingXListener: OnFlingXListener? = null
private var mAnimator: ValueAnimator? = null
private var mGestureDetector: GestureDetector? = null
private var mScroller: Scroller? = null
@ -132,9 +131,6 @@ class CoverLrcView @JvmOverloads constructor(
velocityX: Float,
velocityY: Float
): Boolean {
if (mOnFlingXListener != null && abs(velocityX) > abs(velocityY)) {
return mOnFlingXListener!!.onFlingX(velocityX)
}
if (hasLrc()) {
mScroller!!.fling(
0,
@ -167,7 +163,7 @@ class CoverLrcView @JvmOverloads constructor(
isShowTimeline = false
removeCallbacks(hideTimelineRunnable)
mCurrentLine = centerLine
invalidate()
animateCurrentTextSize()
return true
}
} else {
@ -320,10 +316,6 @@ class CoverLrcView @JvmOverloads constructor(
mOnPlayClickListener = onPlayClickListener
}
fun setOnFlingXListener(onFlingXListener: OnFlingXListener) {
mOnFlingXListener = onFlingXListener
}
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
fun setLabel(label: String?) {
runOnUi {
@ -458,6 +450,7 @@ class CoverLrcView @JvmOverloads constructor(
mCurrentLine = line
if (!isShowTimeline) {
smoothScrollTo(line)
animateCurrentTextSize()
} else {
invalidate()
}
@ -536,6 +529,18 @@ class CoverLrcView @JvmOverloads constructor(
canvas.restore()
}
fun animateCurrentTextSize() {
val currentTextSize = mCurrentTextSize
ValueAnimator.ofFloat(mNormalTextSize, currentTextSize).apply {
addUpdateListener {
mCurrentTextSize = it.animatedValue as Float
invalidate()
}
duration = 300L
start()
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_UP
@ -613,7 +618,7 @@ class CoverLrcView @JvmOverloads constructor(
private fun adjustCenter() {
smoothScrollTo(centerLine, ADJUST_DURATION)
}
/** 滚动到某一行 */
/** 滚动到某一行 */
private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) {
val offset = getOffset(line)

View file

@ -37,6 +37,7 @@ data class CategoryInfo(
Artists(R.id.action_artist, R.string.artists, R.drawable.asld_artist),
Playlists(R.id.action_playlist, R.string.playlists, R.drawable.asld_playlist),
Genres(R.id.action_genre, R.string.genres, R.drawable.asld_guitar),
Folder(R.id.action_folder, R.string.folders, R.drawable.asld_folder);
Folder(R.id.action_folder, R.string.folders, R.drawable.asld_folder),
Search(R.id.action_search, R.string.action_search, R.drawable.ic_search);
}
}

View file

@ -14,7 +14,6 @@
package code.name.monkey.retromusic.preferences
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Bundle
@ -22,8 +21,6 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN
@ -33,6 +30,8 @@ import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreference
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.PreferenceDialogNowPlayingScreenBinding
import code.name.monkey.retromusic.databinding.PreferenceNowPlayingScreenItemBinding
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
import code.name.monkey.retromusic.fragments.AlbumCoverStyle.*
@ -69,14 +68,13 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
private var viewPagerPosition: Int = 0
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@SuppressLint("InflateParams") val view =
layoutInflater
.inflate(R.layout.preference_dialog_now_playing_screen, null)
val viewPager = view.findViewById<ViewPager>(R.id.now_playing_screen_view_pager)
viewPager.adapter = AlbumCoverStyleAdapter(requireContext())
viewPager.addOnPageChangeListener(this)
viewPager.pageMargin = ViewUtil.convertDpToPixel(32f, resources).toInt()
viewPager.currentItem = PreferenceUtil.albumCoverStyle.ordinal
val binding = PreferenceDialogNowPlayingScreenBinding.inflate(layoutInflater)
binding.nowPlayingScreenViewPager.apply {
adapter = AlbumCoverStyleAdapter(requireContext())
addOnPageChangeListener(this@AlbumCoverStylePreferenceDialog)
pageMargin = ViewUtil.convertDpToPixel(32f, resources).toInt()
currentItem = PreferenceUtil.albumCoverStyle.ordinal
}
return materialDialog(R.string.pref_title_album_cover_style)
.setPositiveButton(R.string.set) { _, _ ->
@ -89,7 +87,7 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
PreferenceUtil.albumCoverStyle = coverStyle
}
}
.setView(view)
.setView(binding.root)
.create()
.colorButtons()
}
@ -111,25 +109,17 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
val albumCoverStyle = values()[position]
val inflater = LayoutInflater.from(context)
val layout = inflater.inflate(
R.layout.preference_now_playing_screen_item,
collection,
false
) as ViewGroup
collection.addView(layout)
val binding = PreferenceNowPlayingScreenItemBinding.inflate(inflater, collection, true)
val image = layout.findViewById<ImageView>(R.id.image)
val title = layout.findViewById<TextView>(R.id.title)
val proText = layout.findViewById<TextView>(R.id.proText)
Glide.with(context).load(albumCoverStyle.drawableResId).into(image)
title.setText(albumCoverStyle.titleRes)
Glide.with(context).load(albumCoverStyle.drawableResId).into(binding.image)
binding.title.setText(albumCoverStyle.titleRes)
if (isAlbumCoverStyle(albumCoverStyle)) {
proText.show()
proText.setText(R.string.pro)
binding.proText.show()
binding.proText.setText(R.string.pro)
} else {
proText.hide()
binding.proText.hide()
}
return layout
return binding.root
}
override fun destroyItem(

View file

@ -1,31 +1,17 @@
/*
* 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.preferences
import android.app.Dialog
import android.content.Context
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.AttributeSet
import android.widget.TextView
import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN
import androidx.fragment.app.DialogFragment
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreference
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.PreferenceDialogAudioFadeBinding
import code.name.monkey.retromusic.extensions.addAccentColor
import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.colorControlNormal
import code.name.monkey.retromusic.extensions.materialDialog
@ -50,29 +36,24 @@ class DurationPreference @JvmOverloads constructor(
class DurationPreferenceDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val view = layoutInflater
.inflate(R.layout.preference_dialog_audio_fade, null)
val binding = PreferenceDialogAudioFadeBinding.inflate(layoutInflater)
val slider = view.findViewById<Slider>(R.id.slider)
val duration = view.findViewById<TextView>(R.id.duration)
ColorStateList.valueOf(ThemeStore.accentColor(requireContext())).let {
slider.trackTintList = it
slider.thumbTintList = it
}
slider.value = PreferenceUtil.audioFadeDuration.toFloat()
updateText(slider.value.toInt(), duration)
slider.addOnChangeListener(Slider.OnChangeListener { _, value, fromUser ->
binding.slider.apply {
addAccentColor()
value = PreferenceUtil.audioFadeDuration.toFloat()
updateText(value.toInt(), binding.duration)
addOnChangeListener(Slider.OnChangeListener { _, value, fromUser ->
if (fromUser) {
updateText(value.toInt(), duration)
updateText(value.toInt(), binding.duration)
}
})
}
return materialDialog(R.string.audio_fade_duration)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.save) { _, _ -> updateDuration(slider.value.toInt()) }
.setView(view)
.setPositiveButton(R.string.save) { _, _ -> updateDuration(binding.slider.value.toInt()) }
.setView(binding.root)
.create()
.colorButtons()
}

View file

@ -23,10 +23,10 @@ import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreference
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.CategoryInfoAdapter
import code.name.monkey.retromusic.databinding.PreferenceDialogLibraryCategoriesBinding
import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.colorControlNormal
import code.name.monkey.retromusic.extensions.materialDialog
@ -51,15 +51,14 @@ class LibraryPreference @JvmOverloads constructor(
class LibraryPreferenceDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val view = layoutInflater
.inflate(R.layout.preference_dialog_library_categories, null)
val binding = PreferenceDialogLibraryCategoriesBinding.inflate(layoutInflater)
val categoryAdapter = CategoryInfoAdapter()
val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(activity)
recyclerView.adapter = categoryAdapter
categoryAdapter.attachToRecyclerView(recyclerView)
binding.recyclerView.apply {
layoutManager = LinearLayoutManager(activity)
adapter = categoryAdapter
categoryAdapter.attachToRecyclerView(this)
}
return materialDialog(R.string.library_categories)
.setNeutralButton(
@ -69,7 +68,7 @@ class LibraryPreferenceDialog : DialogFragment() {
}
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.done) { _, _ -> updateCategories(categoryAdapter.categoryInfos) }
.setView(view)
.setView(binding.root)
.create()
.colorButtons()
}

View file

@ -21,8 +21,6 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN
@ -32,6 +30,7 @@ import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreference
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.PreferenceNowPlayingScreenItemBinding
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.NowPlayingScreen
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
@ -116,25 +115,16 @@ private class NowPlayingScreenAdapter(private val context: Context) : PagerAdapt
val nowPlayingScreen = values()[position]
val inflater = LayoutInflater.from(context)
val layout = inflater.inflate(
R.layout.preference_now_playing_screen_item,
collection,
false
) as ViewGroup
collection.addView(layout)
val image = layout.findViewById<ImageView>(R.id.image)
val title = layout.findViewById<TextView>(R.id.title)
val proText = layout.findViewById<TextView>(R.id.proText)
Glide.with(context).load(nowPlayingScreen.drawableResId).into(image)
title.setText(nowPlayingScreen.titleRes)
val binding = PreferenceNowPlayingScreenItemBinding.inflate(inflater, collection, true)
Glide.with(context).load(nowPlayingScreen.drawableResId).into(binding.image)
binding.title.setText(nowPlayingScreen.titleRes)
if (isNowPlayingThemes(nowPlayingScreen)) {
proText.show()
proText.setText(R.string.pro)
binding.proText.show()
binding.proText.setText(R.string.pro)
} else {
proText.hide()
binding.proText.hide()
}
return layout
return binding.root
}
override fun destroyItem(

View file

@ -69,6 +69,7 @@ interface Repository {
suspend fun recentAlbumsHome(): Home
suspend fun favoritePlaylistHome(): Home
suspend fun suggestionsHome(): Home
suspend fun suggestions(): List<Song>
suspend fun genresHome(): Home
suspend fun playlists(): Home
suspend fun homeSections(): List<Home>
@ -240,21 +241,17 @@ class RealRepository(
override suspend fun homeSections(): List<Home> {
val homeSections = mutableListOf<Home>()
val sections: List<Home> = listOf(
suggestionsHome(),
topArtistsHome(),
topAlbumsHome(),
recentArtistsHome(),
recentAlbumsHome(),
favoritePlaylistHome()
// genresHome()
)
for (section in sections) {
if (section.arrayList.isNotEmpty()) {
if (section.homeSection != SUGGESTIONS || PreferenceUtil.homeSuggestions) {
homeSections.add(section)
}
}
}
return homeSections
}
@ -374,6 +371,13 @@ class RealRepository(
return suggestions
}
override suspend fun suggestions(): List<Song> {
if (!PreferenceUtil.homeSuggestions) return listOf<Song>()
return NotPlayedPlaylist().songs().shuffled().takeIf {
it.size > 9
} ?: emptyList()
}
override suspend fun genresHome(): Home {
val genres = genreRepository.genres().shuffled()
return Home(genres, GENRES, R.string.genres)

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.repository
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns
@ -42,11 +43,13 @@ interface SongRepository {
fun songs(query: String): List<Song>
fun songsByFilePath(filePath: String): List<Song>
fun songsByFilePath(filePath: String, ignoreBlacklist: Boolean = false): List<Song>
fun song(cursor: Cursor?): Song
fun song(songId: Long): Song
fun songsIgnoreBlacklist(uri: Uri): List<Song>
}
class RealSongRepository(private val context: Context) : SongRepository {
@ -84,15 +87,38 @@ class RealSongRepository(private val context: Context) : SongRepository {
return song(makeSongCursor(AudioColumns._ID + "=?", arrayOf(songId.toString())))
}
override fun songsByFilePath(filePath: String): List<Song> {
override fun songsByFilePath(filePath: String, ignoreBlacklist: Boolean): List<Song> {
return songs(
makeSongCursor(
AudioColumns.DATA + "=?",
arrayOf(filePath)
arrayOf(filePath),
ignoreBlacklist = ignoreBlacklist
)
)
}
override fun songsIgnoreBlacklist(uri: Uri): List<Song> {
var filePath = ""
context.contentResolver.query(
uri,
arrayOf(AudioColumns.DATA),
null,
null,
null
).use { cursor ->
if (cursor != null) {
if (cursor.count != 0) {
cursor.moveToFirst()
filePath = cursor.getString(AudioColumns.DATA)
println("File Path: $filePath")
}
}
}
return songsByFilePath(
filePath, true
)
}
private fun getSongFromCursorImpl(
cursor: Cursor
): Song {
@ -128,13 +154,14 @@ class RealSongRepository(private val context: Context) : SongRepository {
@JvmOverloads
fun makeSongCursor(
selection: String?,
selectionValues: Array<String>?,
sortOrder: String = PreferenceUtil.songSortOrder
sortOrder: String = PreferenceUtil.songSortOrder,
ignoreBlacklist: Boolean = false
): Cursor? {
var selectionFinal = selection
var selectionValuesFinal = selectionValues
if (!ignoreBlacklist) {
selectionFinal = if (selection != null && selection.trim { it <= ' ' } != "") {
"$IS_MUSIC AND $selectionFinal"
} else {
@ -161,7 +188,7 @@ class RealSongRepository(private val context: Context) : SongRepository {
selectionFinal =
selectionFinal + " AND " + Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000)
}
val uri = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {

View file

@ -16,8 +16,10 @@ package code.name.monkey.retromusic.service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.PlaybackParams;
import android.media.audiofx.AudioEffect;
import android.net.Uri;
import android.os.PowerManager;
@ -26,14 +28,19 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import code.name.monkey.appthemehelper.util.VersionUtils;
import code.name.monkey.retromusic.ConstantsKt;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.service.playback.Playback;
import code.name.monkey.retromusic.util.PreferenceUtil;
/** @author Andrew Neal, Karim Abou Zeid (kabouzeid) */
/**
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
*/
public class MultiPlayer
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = MultiPlayer.class.getSimpleName();
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
@ -45,10 +52,13 @@ public class MultiPlayer
private boolean mIsInitialized = false;
/** Constructor of <code>MultiPlayer</code> */
/**
* Constructor of <code>MultiPlayer</code>
*/
MultiPlayer(final Context context) {
this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
PreferenceManager.getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(this);
}
/**
@ -82,6 +92,7 @@ public class MultiPlayer
} else {
player.setDataSource(path);
}
setPlaybackSpeedPitch(player);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.prepare();
} catch (Exception e) {
@ -155,13 +166,17 @@ public class MultiPlayer
this.callbacks = callbacks;
}
/** @return True if the player is ready to go, false otherwise */
/**
* @return True if the player is ready to go, false otherwise
*/
@Override
public boolean isInitialized() {
return mIsInitialized;
}
/** Starts or resumes playback. */
/**
* Starts or resumes playback.
*/
@Override
public boolean start() {
try {
@ -172,14 +187,18 @@ public class MultiPlayer
}
}
/** Resets the MediaPlayer to its uninitialized state. */
/**
* Resets the MediaPlayer to its uninitialized state.
*/
@Override
public void stop() {
mCurrentMediaPlayer.reset();
mIsInitialized = false;
}
/** Releases resources associated with this MediaPlayer object. */
/**
* Releases resources associated with this MediaPlayer object.
*/
@Override
public void release() {
stop();
@ -187,9 +206,12 @@ public class MultiPlayer
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
}
PreferenceManager.getDefaultSharedPreferences(context).unregisterOnSharedPreferenceChangeListener(this);
}
/** Pauses playback. Call start() to resume. */
/**
* Pauses playback. Call start() to resume.
*/
@Override
public boolean pause() {
try {
@ -200,7 +222,9 @@ public class MultiPlayer
}
}
/** Checks whether the MultiPlayer is playing. */
/**
* Checks whether the MultiPlayer is playing.
*/
@Override
public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying();
@ -291,7 +315,9 @@ public class MultiPlayer
return mCurrentMediaPlayer.getAudioSessionId();
}
/** {@inheritDoc} */
/**
* {@inheritDoc}
*/
@Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false;
@ -308,7 +334,9 @@ public class MultiPlayer
return false;
}
/** {@inheritDoc} */
/**
* {@inheritDoc}
*/
@Override
public void onCompletion(final MediaPlayer mp) {
if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) {
@ -324,5 +352,25 @@ public class MultiPlayer
}
@Override
public void setCrossFadeDuration(int duration) { }
public void setCrossFadeDuration(int duration) {
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(ConstantsKt.PLAYBACK_SPEED) || key.equals(ConstantsKt.PLAYBACK_PITCH)) {
setPlaybackSpeedPitch(mCurrentMediaPlayer);
}
}
public void setPlaybackSpeedPitch(MediaPlayer mp) {
if (VersionUtils.INSTANCE.hasMarshmallow()) {
boolean wasPlaying = mp.isPlaying();
mp.setPlaybackParams(new PlaybackParams()
.setSpeed(PreferenceUtil.INSTANCE.getPlaybackSpeed())
.setPitch(PreferenceUtil.INSTANCE.getPlaybackPitch()));
if (!wasPlaying) {
if (mp.isPlaying()) mp.pause();
}
}
}
}

View file

@ -22,6 +22,7 @@ import static code.name.monkey.retromusic.ConstantsKt.BLURRED_ALBUM_ART;
import static code.name.monkey.retromusic.ConstantsKt.CLASSIC_NOTIFICATION;
import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION;
import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
import static code.name.monkey.retromusic.ConstantsKt.PLAYBACK_SPEED;
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
@ -82,6 +83,7 @@ import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.activities.LockScreenActivity;
import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
import code.name.monkey.retromusic.appwidgets.AppWidgetCard;
import code.name.monkey.retromusic.appwidgets.AppWidgetCircle;
import code.name.monkey.retromusic.appwidgets.AppWidgetClassic;
import code.name.monkey.retromusic.appwidgets.AppWidgetMD3;
import code.name.monkey.retromusic.appwidgets.AppWidgetSmall;
@ -139,6 +141,7 @@ public class MusicService extends MediaBrowserServiceCompat
public static final String META_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".metachanged";
public static final String QUEUE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".queuechanged";
public static final String PLAY_STATE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".playstatechanged";
public static final String FAVORITE_STATE_CHANGED =
RETRO_MUSIC_PACKAGE_NAME + "favoritestatechanged";
public static final String REPEAT_MODE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".repeatmodechanged";
@ -204,6 +207,8 @@ public class MusicService extends MediaBrowserServiceCompat
private final AppWidgetMD3 appWidgetMd3 = AppWidgetMD3.Companion.getInstance();
private final AppWidgetCircle appWidgetCircle = AppWidgetCircle.Companion.getInstance();
private final BroadcastReceiver widgetIntentReceiver =
new BroadcastReceiver() {
@Override
@ -236,6 +241,10 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetMd3.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetCircle.NAME: {
appWidgetCircle.performUpdate(MusicService.this, ids);
break;
}
}
}
}
@ -285,6 +294,7 @@ public class MusicService extends MediaBrowserServiceCompat
public void onReceive(final Context context, final Intent intent) {
playingNotification.updateFavorite(getCurrentSong(), MusicService.this::startForegroundOrNotify);
startForegroundOrNotify();
appWidgetCircle.notifyChange(MusicService.this, FAVORITE_STATE_CHANGED);
}
};
private final BroadcastReceiver lockScreenReceiver =
@ -431,9 +441,7 @@ public class MusicService extends MediaBrowserServiceCompat
registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
setSessionToken(mediaSession.getSessionToken());
if (VersionUtils.INSTANCE.hasMarshmallow()) {
notificationManager = getSystemService(NotificationManager.class);
}
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
initNotification();
mediaStoreObserver = new MediaStoreObserver(this, playerHandler);
@ -939,8 +947,11 @@ public class MusicService extends MediaBrowserServiceCompat
break;
case CLASSIC_NOTIFICATION:
updateNotification();
playingNotification.setPlaying(isPlaying());
playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify);
playingNotification.setPlaying(isPlaying(),this::startForegroundOrNotify);
break;
case PLAYBACK_SPEED:
updateMediaSessionPlaybackState();
break;
case TOGGLE_HEADSET:
registerHeadsetEvents();
@ -1336,7 +1347,7 @@ public class MusicService extends MediaBrowserServiceCompat
.setState(
isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
getSongProgressMillis(),
1);
PreferenceUtil.INSTANCE.getPlaybackSpeed());
setCustomAction(stateBuilder);
@ -1426,7 +1437,7 @@ public class MusicService extends MediaBrowserServiceCompat
savePositionInTrack();
}
songPlayCountHelper.notifyPlayStateChanged(isPlaying);
playingNotification.setPlaying(isPlaying);
playingNotification.setPlaying(isPlaying, this::startForegroundOrNotify);
startForegroundOrNotify();
break;
case FAVORITE_STATE_CHANGED:
@ -1617,6 +1628,7 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetCard.notifyChange(this, what);
appWidgetText.notifyChange(this, what);
appWidgetMd3.notifyChange(this, what);
appWidgetCircle.notifyChange(this, what);
}
private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) {

View file

@ -29,7 +29,7 @@ abstract class PlayingNotification(context: Context) :
abstract fun updateMetadata(song: Song, onUpdate: () -> Unit)
abstract fun setPlaying(isPlaying: Boolean)
abstract fun setPlaying(isPlaying: Boolean, onUpdate: () -> Unit)
abstract fun updateFavorite(song: Song, onUpdate: () -> Unit)

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