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

View file

@ -292,6 +292,18 @@
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/app_widget_md3_info" /> android:resource="@xml/app_widget_md3_info" />
</receiver> </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 <service
android:name=".service.MusicService" android:name=".service.MusicService"

View file

@ -70,6 +70,8 @@
title="KeyboardVisibilityEvent">KeyboardVisibilityEvent</a></b> by Yasuhiro SHIMIZU</p> title="KeyboardVisibilityEvent">KeyboardVisibilityEvent</a></b> by Yasuhiro SHIMIZU</p>
<p><b><a href="https://github.com/JetradarMobile/android-snowfall" <p><b><a href="https://github.com/JetradarMobile/android-snowfall"
title="android-snowfall">android-snowfall</a></b> by Jetradar Mobile</p> 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://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><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b>
</p> </p>

View file

@ -58,12 +58,41 @@
} }
{style-placeholder} {style-placeholder}
</style> </style>
</head> </head>
<body> <body>
<div> <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> <h2>v5.6.1</h2>
<h3>Fixed</h3> <h3>Fixed</h3>
<ul> <ul>

View file

@ -20,6 +20,7 @@ import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager 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.BillingProcessor
import com.anjlab.android.iab.v3.PurchaseInfo import com.anjlab.android.iab.v3.PurchaseInfo
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
@ -28,6 +29,7 @@ import org.koin.core.context.startKoin
class App : Application() { class App : Application() {
lateinit var billingProcessor: BillingProcessor lateinit var billingProcessor: BillingProcessor
private val wallpaperAccentManager = WallpaperAccentManager(this)
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -44,6 +46,7 @@ class App : Application() {
.coloredNavigationBar(true) .coloredNavigationBar(true)
.commit() .commit()
} }
wallpaperAccentManager.init()
if (VersionUtils.hasNougatMR()) if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).initDynamicShortcuts() DynamicShortcutManager(this).initDynamicShortcuts()
@ -71,6 +74,7 @@ class App : Application() {
override fun onTerminate() { override fun onTerminate() {
super.onTerminate() super.onTerminate()
billingProcessor.release() billingProcessor.release()
wallpaperAccentManager.release()
} }
companion object { 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 CAROUSEL_EFFECT = "carousel_effect"
const val COLORED_NOTIFICATION = "colored_notification" const val COLORED_NOTIFICATION = "colored_notification"
const val CLASSIC_NOTIFICATION = "classic_notification" const val CLASSIC_NOTIFICATION = "classic_notification"
const val GAP_LESS_PLAYBACK = "gap_less_playback" const val GAP_LESS_PLAYBACK = "gapless_playback"
const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen" const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen"
const val BLURRED_ALBUM_ART = "blurred_album_art" const val BLURRED_ALBUM_ART = "blurred_album_art"
const val NEW_BLUR_AMOUNT = "new_blur_amount" const val NEW_BLUR_AMOUNT = "new_blur_amount"
@ -154,3 +154,11 @@ const val LAST_USED_TAB = "last_used_tab"
const val WHITELIST_MUSIC = "whitelist_music" const val WHITELIST_MUSIC = "whitelist_music"
const val MATERIAL_YOU = "material_you" const val MATERIAL_YOU = "material_you"
const val SNOWFALL = "snowfall" const val SNOWFALL = "snowfall"
const val LYRICS_TYPE = "lyrics_type"
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

@ -62,7 +62,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
if (!hasPermissions()) { if (!hasPermissions()) {
findNavController(R.id.fragment_container).navigate(R.id.permissionFragment) findNavController(R.id.fragment_container).navigate(R.id.permissionFragment)
} }
if (BuildConfig.VERSION_CODE > PreferenceUtil.lastVersion && !BuildConfig.DEBUG){ if (BuildConfig.VERSION_CODE > PreferenceUtil.lastVersion && !BuildConfig.DEBUG) {
NavigationUtil.gotoWhatNews(this) NavigationUtil.gotoWhatNews(this)
} }
} }
@ -103,7 +103,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
currentFragment(R.id.fragment_container)?.enterTransition = null currentFragment(R.id.fragment_container)?.enterTransition = null
} }
when (destination.id) { 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 // Save the last tab
if (PreferenceUtil.rememberLastTab) { if (PreferenceUtil.rememberLastTab) {
saveTab(destination.id) saveTab(destination.id)
@ -147,7 +147,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { 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() postRecreate()
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -14,12 +14,11 @@
*/ */
package code.name.monkey.retromusic.adapter package code.name.monkey.retromusic.adapter
import android.os.SystemClock import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
@ -29,31 +28,22 @@ import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
import code.name.monkey.retromusic.adapter.song.SongAdapter 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.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.IAlbumClickListener
import code.name.monkey.retromusic.interfaces.IArtistClickListener import code.name.monkey.retromusic.interfaces.IArtistClickListener
import code.name.monkey.retromusic.interfaces.IGenreClickListener import code.name.monkey.retromusic.interfaces.IGenreClickListener
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.material.card.MaterialCardView
class HomeAdapter( class HomeAdapter(
private val activity: AppCompatActivity private val activity: AppCompatActivity
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IArtistClickListener, IAlbumClickListener, ) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IArtistClickListener, IAlbumClickListener,
IGenreClickListener { IGenreClickListener {
private var mLastClickTime: Long = 0
private var list = listOf<Home>() private var list = listOf<Home>()
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
@ -65,17 +55,10 @@ class HomeAdapter(
LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false) LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
return when (viewType) { return when (viewType) {
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout) RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
GENRES -> GenreViewHolder(layout)
FAVOURITES -> PlaylistViewHolder(layout) FAVOURITES -> PlaylistViewHolder(layout)
TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout) TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout)
else -> { else -> {
SuggestionsViewHolder( ArtistViewHolder(layout)
LayoutInflater.from(activity).inflate(
R.layout.item_suggestions,
parent,
false
)
)
} }
} }
} }
@ -127,10 +110,6 @@ class HomeAdapter(
) )
} }
} }
SUGGESTIONS -> {
val viewHolder = holder as SuggestionsViewHolder
viewHolder.bindView(home)
}
FAVOURITES -> { FAVOURITES -> {
val viewHolder = holder as PlaylistViewHolder val viewHolder = holder as PlaylistViewHolder
viewHolder.bindView(home) 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 return list.size
} }
@SuppressLint("NotifyDataSetChanged")
fun swapData(sections: List<Home>) { fun swapData(sections: List<Home>) {
list = sections list = sections
notifyDataSetChanged() 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) { private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(home: Home) { fun bindView(home: Home) {
title.setText(home.titleRes) 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) { open class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView) val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
val title: AppCompatTextView = itemView.findViewById(R.id.title) val title: AppCompatTextView = itemView.findViewById(R.id.title)

View file

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

View file

@ -20,6 +20,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
@ -78,7 +79,7 @@ class SongFileAdapter(
if (holder.itemViewType == FILE) { if (holder.itemViewType == FILE) {
holder.text?.text = getFileText(file) holder.text?.text = getFileText(file)
} else { } 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.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
@ -174,7 +175,7 @@ open class AlbumAdapter(
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init { init {
menu?.visibility = View.GONE menu?.isVisible = false
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {

View file

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

View file

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

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.adapter.song
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
@ -129,7 +130,7 @@ class OrderablePlaylistSongAdapter(
} }
init { init {
dragView?.visibility = View.VISIBLE dragView?.isVisible = true
} }
override fun onClick(v: View?) { 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.MenuItem
import android.view.View import android.view.View
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
@ -154,7 +155,7 @@ class PlayingQueueAdapter(
} }
init { init {
dragView?.visibility = View.VISIBLE dragView?.isVisible = true
} }
override fun onSongMenuItemClick(item: MenuItem): Boolean { override fun onSongMenuItemClick(item: MenuItem): Boolean {

View file

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

View file

@ -116,7 +116,7 @@ open class SongAdapter(
holder.title?.setTextColor(color.primaryTextColor) holder.title?.setTextColor(color.primaryTextColor)
holder.text?.setTextColor(color.secondaryTextColor) holder.text?.setTextColor(color.secondaryTextColor)
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor) holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
holder.menu?.imageTintList= ColorStateList.valueOf(color.primaryTextColor) holder.menu?.imageTintList = ColorStateList.valueOf(color.primaryTextColor)
} }
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor) holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
} }

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 image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = createRoundedBitmap( val roundedBitmap = createRoundedBitmap(
image, imageSize, imageSize, cardRadius, cardRadius, cardRadius, cardRadius image,
imageSize,
imageSize,
cardRadius,
cardRadius,
cardRadius,
cardRadius
) )
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap) appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)

View file

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

View file

@ -4,14 +4,11 @@ import android.app.ActivityManager
import android.graphics.Color import android.graphics.Color
import android.os.Build import android.os.Build
import android.view.View import android.view.View
import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat import androidx.core.view.*
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isGone
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R 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() { fun AppCompatActivity.setImmersiveFullscreen() {
if (PreferenceUtil.isFullScreenMode) { if (PreferenceUtil.isFullScreenMode) {
WindowInsetsControllerCompat(window, window.decorView).apply { WindowInsetsControllerCompat(window, window.decorView).apply {
@ -36,6 +49,14 @@ fun AppCompatActivity.setImmersiveFullscreen() {
window.attributes.layoutInDisplayCutoutMode = window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 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() { fun AppCompatActivity.setDrawBehindSystemBars() {
WindowCompat.setDecorFitsSystemWindows(window, false)
if (VersionUtils.hasOreo()) { if (VersionUtils.hasOreo()) {
WindowCompat.setDecorFitsSystemWindows(window, false)
window.navigationBarColor = Color.TRANSPARENT
window.statusBarColor = Color.TRANSPARENT
if (VersionUtils.hasQ()) { if (VersionUtils.hasQ()) {
window.isNavigationBarContrastEnforced = false window.isNavigationBarContrastEnforced = false
} }
window.navigationBarColor = Color.TRANSPARENT
window.statusBarColor = Color.TRANSPARENT
} else { } else {
setNavigationBarColorPreOreo(surfaceColor()) setNavigationBarColorPreOreo(surfaceColor())
if (VersionUtils.hasMarshmallow()) { if (VersionUtils.hasMarshmallow()) {
setStatusBarColor(Color.TRANSPARENT) setStatusBarColor(Color.TRANSPARENT)
} else { } else {
setStatusBarColor(surfaceColor()) setStatusBarColor(Color.BLACK)
} }
} }
} }
@ -96,24 +117,49 @@ fun AppCompatActivity.setTaskDescriptionColorAuto() {
setTaskDescriptionColor(surfaceColor()) setTaskDescriptionColor(surfaceColor())
} }
fun AppCompatActivity.setLightNavigationAuto() { @Suppress("Deprecation")
ATH.setLightNavigationBarAuto(this, surfaceColor()) 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) { fun AppCompatActivity.setLightStatusBarAuto() {
ATH.setLightStatusBar(this, enabled) setLightStatusBar(surfaceColor().isColorLight)
} }
fun AppCompatActivity.setLightStatusBarAuto(bgColor: Int) { fun AppCompatActivity.setLightStatusBarAuto(bgColor: Int) {
setLightStatusBar(ColorUtil.isColorLight(bgColor)) setLightStatusBar(bgColor.isColorLight)
} }
@Suppress("Deprecation")
fun AppCompatActivity.setLightNavigationBar(enabled: Boolean) { 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) { 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.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.CircularProgressIndicator import com.google.android.material.progressindicator.CircularProgressIndicator
import com.google.android.material.slider.Slider
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
fun Int.ripAlpha(): Int { fun Int.ripAlpha(): Int {
@ -112,6 +113,22 @@ fun SeekBar.addAccentColor() {
thumbTintList = colorState 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() { fun Button.accentTextColor() {
if (materialYou) return if (materialYou) return
setTextColor(ThemeStore.accentColor(App.getContext())) setTextColor(ThemeStore.accentColor(App.getContext()))
@ -248,15 +265,10 @@ fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
@ColorInt @ColorInt
fun Context.darkAccentColor(): Int { fun Context.darkAccentColor(): Int {
val colorSurfaceVariant = if (surfaceColor().isColorLight) {
surfaceColor()
} else {
surfaceColor().lighterColor
}
return ColorUtils.blendARGB( return ColorUtils.blendARGB(
accentColor(), accentColor(),
colorSurfaceVariant, surfaceColor(),
if (surfaceColor().isColorLight) 0.96f else 0.975f 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 inline val @receiver:ColorInt Int.isColorLight
get() = ColorUtil.isColorLight(this) get() = ColorUtil.isColorLight(this)
@ -276,4 +297,7 @@ inline val @receiver:ColorInt Int.lighterColor
get() = ColorUtil.lightenColor(this) get() = ColorUtil.lightenColor(this)
inline val @receiver:ColorInt Int.darkerColor inline val @receiver:ColorInt Int.darkerColor
get() = ColorUtil.darkenColor(this) 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.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
fun DialogFragment.materialDialog(title: Int): MaterialAlertDialogBuilder { fun DialogFragment.materialDialog(title: Int): MaterialAlertDialogBuilder {

View file

@ -21,9 +21,9 @@ fun Fragment.createNewFile(
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{ result: ActivityResult -> { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) { if (result.resultCode == Activity.RESULT_OK) {
val outputStream: OutputStream? = context?.contentResolver?.openOutputStream(result.data?.data!!)?.use { os->
context?.contentResolver?.openOutputStream(result.data?.data!!) write(os, result.data?.data)
write(outputStream, 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.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.annotation.Px
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart import androidx.core.animation.doOnStart
import androidx.core.view.* 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.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil 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.bottomsheet.BottomSheetBehavior
import com.google.android.material.imageview.ShapeableImageView import dev.chrisbanes.insetter.applyInsetter
import com.google.android.material.shape.ShapeAppearanceModel
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T : View> ViewGroup.inflate(@LayoutRes layout: Int): T { 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() { fun View.show() {
visibility = View.VISIBLE isVisible = true
} }
fun View.hide() { fun View.hide() {
visibility = View.GONE isVisible = false
} }
fun View.hidden() { fun View.hidden() {
visibility = View.INVISIBLE isInvisible = true
} }
fun View.showOrHide(show: Boolean) = if (show) show() else hide()
fun EditText.appHandleColor(): EditText { fun EditText.appHandleColor(): EditText {
if (PreferenceUtil.materialYou) return this if (PreferenceUtil.materialYou) return this
TintHelper.colorHandles(this, ThemeStore.accentColor(context)) 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. * This will draw our view above the navigation bar instead of behind it by adding margins.
*/ */
fun View.drawAboveSystemBars(onlyPortrait: Boolean = true) { fun View.drawAboveSystemBars(onlyPortrait: Boolean = true) {
if (PreferenceUtil.isFullScreenMode) return if (PreferenceUtil.isFullScreenMode) return
if (onlyPortrait && RetroUtil.isLandscape()) return if (onlyPortrait && RetroUtil.isLandscape()) return
// Create a snapshot of the view's margin state applyInsetter {
val initialMargin = recordInitialMarginForView(this) type(navigationBars = true) {
ViewCompat.setOnApplyWindowInsetsListener( margin()
(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
} }
windowInsets
} }
} }
/** /**
* This will draw our view above the navigation bar instead of behind it by adding padding. * 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 if (PreferenceUtil.isFullScreenMode) return
val initialPadding = recordInitialPaddingForView(this) val initialPadding = recordInitialPaddingForView(this)
@ -168,11 +173,9 @@ fun View.drawAboveSystemBarsWithPadding(consume: Boolean = false) {
) { v: View, windowInsets: WindowInsetsCompat -> ) { v: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
v.updatePadding( v.updatePadding(
left = initialPadding.left + insets.left, bottom = initialPadding.bottom + insets.bottom
bottom = initialPadding.bottom + insets.bottom,
right = initialPadding.right + insets.right
) )
if (consume) WindowInsetsCompat.CONSUMED else windowInsets windowInsets
} }
requestApplyInsetsWhenAttached() 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( data class InitialPadding(
val left: Int, val top: Int, val left: Int, val top: Int,
val right: Int, val bottom: Int val right: Int, val bottom: Int
@ -243,4 +205,4 @@ data class InitialPadding(
fun recordInitialPaddingForView(view: View) = InitialPadding( fun recordInitialPaddingForView(view: View) = InitialPadding(
view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom
) )

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.widget.Toast import android.widget.Toast
import androidx.core.animation.doOnEnd
import androidx.lifecycle.* import androidx.lifecycle.*
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.db.* import code.name.monkey.retromusic.db.*
@ -39,6 +40,7 @@ class LibraryViewModel(
private val _paletteColor = MutableLiveData<Int>() private val _paletteColor = MutableLiveData<Int>()
private val home = MutableLiveData<List<Home>>() private val home = MutableLiveData<List<Home>>()
private val suggestions = MutableLiveData<List<Song>>()
private val albums = MutableLiveData<List<Album>>() private val albums = MutableLiveData<List<Album>>()
private val songs = MutableLiveData<List<Song>>() private val songs = MutableLiveData<List<Song>>()
private val artists = MutableLiveData<List<Artist>>() private val artists = MutableLiveData<List<Artist>>()
@ -55,6 +57,7 @@ class LibraryViewModel(
private fun loadLibraryContent() = viewModelScope.launch(IO) { private fun loadLibraryContent() = viewModelScope.launch(IO) {
fetchHomeSections() fetchHomeSections()
fetchSuggestions()
fetchSongs() fetchSongs()
fetchAlbums() fetchAlbums()
fetchArtists() fetchArtists()
@ -92,38 +95,33 @@ class LibraryViewModel(
return home return home
} }
fun getSuggestions(): LiveData<List<Song>> {
return suggestions
}
fun getFabMargin(): LiveData<Int> { fun getFabMargin(): LiveData<Int> {
return fabMargin return fabMargin
} }
private fun fetchSongs() { private suspend fun fetchSongs() {
viewModelScope.launch(IO) { songs.postValue(repository.allSongs())
songs.postValue(repository.allSongs())
}
} }
private fun fetchAlbums() { private suspend fun fetchAlbums() {
viewModelScope.launch(IO) { albums.postValue(repository.fetchAlbums())
albums.postValue(repository.fetchAlbums())
}
} }
private fun fetchArtists() { private suspend fun fetchArtists() {
if (PreferenceUtil.albumArtistsOnly) { if (PreferenceUtil.albumArtistsOnly) {
viewModelScope.launch(IO) { artists.postValue(repository.albumArtists())
artists.postValue(repository.albumArtists())
}
} else { } else {
viewModelScope.launch(IO) { artists.postValue(repository.fetchArtists())
artists.postValue(repository.fetchArtists())
}
} }
} }
private fun fetchPlaylists() { private suspend fun fetchPlaylists() {
viewModelScope.launch(IO) { playlists.postValue(repository.fetchPlaylistWithSongs())
playlists.postValue(repository.fetchPlaylistWithSongs())
}
} }
private fun fetchLegacyPlaylist() { private fun fetchLegacyPlaylist() {
@ -132,16 +130,16 @@ class LibraryViewModel(
} }
} }
private fun fetchGenres() { private suspend fun fetchGenres() {
viewModelScope.launch(IO) { genres.postValue(repository.fetchGenres())
genres.postValue(repository.fetchGenres())
}
} }
fun fetchHomeSections() { private suspend fun fetchHomeSections() {
viewModelScope.launch(IO) { home.postValue(repository.homeSections())
home.postValue(repository.homeSections()) }
}
private suspend fun fetchSuggestions() {
suggestions.postValue(repository.suggestions())
} }
fun search(query: String?, filter: Filter) { 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) { when (reloadType) {
Songs -> fetchSongs() Songs -> fetchSongs()
Albums -> fetchAlbums() Albums -> fetchAlbums()
@ -159,6 +157,7 @@ class LibraryViewModel(
HomeSections -> fetchHomeSections() HomeSections -> fetchHomeSections()
Playlists -> fetchPlaylists() Playlists -> fetchPlaylists()
Genres -> fetchGenres() Genres -> fetchGenres()
Suggestions -> fetchSuggestions()
} }
} }
@ -255,7 +254,7 @@ class LibraryViewModel(
} }
repository.insertSongs(songEntities) repository.insertSongs(songEntities)
} else { } else {
if (playlist != Playlist.empty){ if (playlist != Playlist.empty) {
val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name)) val playListId = createPlaylist(PlaylistEntity(playlistName = playlist.name))
val songEntities = playlist.getSongs().map { val songEntities = playlist.getSongs().map {
it.toSongEntity(playListId) it.toSongEntity(playListId)
@ -367,7 +366,7 @@ class LibraryViewModel(
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
Toast.makeText( Toast.makeText(
App.getContext(), App.getContext(),
"Adding songs to $playlistName", "Adding songs to $playlistName",
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
@ -382,15 +381,16 @@ class LibraryViewModel(
fun setFabMargin(bottomMargin: Int) { fun setFabMargin(bottomMargin: Int) {
val currentValue = DensityUtil.dip2px(App.getContext(), 16F) + val currentValue = DensityUtil.dip2px(App.getContext(), 16F) +
bottomMargin bottomMargin
if (currentValue != fabMargin.value) { ValueAnimator.ofInt(fabMargin.value!!, currentValue).apply {
ValueAnimator.ofInt(fabMargin.value!!, currentValue).apply { addUpdateListener {
addUpdateListener { fabMargin.postValue(
fabMargin.postValue( (it.animatedValue as Int)
it.animatedValue as Int )
)
}
start()
} }
doOnEnd {
fabMargin.postValue(currentValue)
}
start()
} }
} }
} }
@ -402,4 +402,5 @@ enum class ReloadType {
HomeSections, HomeSections,
Playlists, Playlists,
Genres, 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 { sharedElementEnterTransition = MaterialContainerTransform().apply {
drawingViewId = R.id.fragment_container drawingViewId = R.id.fragment_container
scrimColor = Color.TRANSPARENT scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface)) setAllContainerColors(surfaceColor())
setPathMotion(MaterialArcMotion()) setPathMotion(MaterialArcMotion())
} }
} }

View file

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

View file

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

View file

@ -58,7 +58,7 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) { if (!handleBackPress()) {
remove() remove()
mainActivity.finish() requireActivity().onBackPressed()
} }
} }
} }
@ -382,4 +382,11 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
super.onResume() super.onResume()
libraryViewModel.forceReload(ReloadType.Artists) 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 val backupsLiveData: LiveData<List<File>> = backupsMutableLiveData
fun loadBackups(context: Context) { fun loadBackups(context: Context) {
BackupHelper.getBackupRoot(context).listFiles { _, name -> BackupHelper.getBackupRoot().listFiles { _, name ->
return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION) return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION)
}?.toList()?.let { }?.toList()?.let {
backupsMutableLiveData.value = it backupsMutableLiveData.value = it

View file

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

View file

@ -14,14 +14,27 @@
*/ */
package code.name.monkey.retromusic.fragments.base 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.os.Bundle
import android.view.View import android.view.View
import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator 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.annotation.LayoutRes
import androidx.core.view.isVisible
import code.name.monkey.retromusic.R 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.fragments.other.VolumeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.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.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -36,37 +49,101 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
protected abstract fun hide() protected abstract fun hide()
protected abstract fun updateShuffleState()
protected abstract fun updateRepeatState()
protected abstract fun setUpProgressSlider()
abstract fun setColor(color: MediaNotificationProcessor) abstract fun setColor(color: MediaNotificationProcessor)
fun showBounceAnimation(view: View) { var lastPlaybackControlsColor: Int = 0
view.apply {
clearAnimation() var lastDisabledPlaybackControlsColor: Int = 0
scaleX = 0.9f
scaleY = 0.9f var isSeeking = false
visibility = View.VISIBLE private set
pivotX = (view.width / 2).toFloat()
pivotY = (view.height / 2).toFloat() 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()
}
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()
} }
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
isVisible = true
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 onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -74,6 +151,68 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
hideVolumeIfAvailable() 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 protected var volumeFragment: VolumeFragment? = null
private fun hideVolumeIfAvailable() { private fun hideVolumeIfAvailable() {
@ -82,10 +221,20 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
.replace(R.id.volumeFragmentContainer, VolumeFragment()).commit() .replace(R.id.volumeFragmentContainer, VolumeFragment()).commit()
childFragmentManager.executePendingTransactions() childFragmentManager.executePendingTransactions()
volumeFragment = 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 { companion object {
const val SLIDER_ANIMATION_TIME: Long = 400 const val SLIDER_ANIMATION_TIME: Long = 400
} }

View file

@ -32,13 +32,14 @@ import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.appcompat.graphics.drawable.DrawableWrapper
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.navOptions import androidx.navigation.navOptions
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.EXTRA_ARTIST_ID
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
@ -50,6 +51,7 @@ import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.dialogs.* import code.name.monkey.retromusic.dialogs.*
import code.name.monkey.retromusic.extensions.currentFragment import code.name.monkey.retromusic.extensions.currentFragment
import code.name.monkey.retromusic.extensions.hide 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.extensions.whichFragment
import code.name.monkey.retromusic.fragments.NowPlayingScreen import code.name.monkey.retromusic.fragments.NowPlayingScreen
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
@ -78,9 +80,18 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
): Boolean { ): Boolean {
val song = MusicPlayerRemote.currentSong val song = MusicPlayerRemote.currentSong
when (item.itemId) { when (item.itemId) {
R.id.action_playback_speed -> {
PlaybackSpeedDialog.newInstance().show(childFragmentManager, "PLAYBACK_SETTINGS")
return true
}
R.id.action_toggle_lyrics -> { R.id.action_toggle_lyrics -> {
PreferenceUtil.showLyrics = !item.isChecked PreferenceUtil.showLyrics = !PreferenceUtil.showLyrics
item.isChecked = !item.isChecked showLyricsIcon(item)
if (PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) {
mainActivity.keepScreenOn(true)
} else if (!PreferenceUtil.isScreenOnEnabled && !PreferenceUtil.showLyrics) {
mainActivity.keepScreenOn(false)
}
return true return true
} }
R.id.action_go_to_lyrics -> { R.id.action_go_to_lyrics -> {
@ -162,7 +173,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return true return true
} }
R.id.action_sleep_timer -> { R.id.action_sleep_timer -> {
SleepTimerDialog().show(parentFragmentManager, TAG) SleepTimerDialog().show(parentFragmentManager, "SLEEP_TIMER")
return true return true
} }
R.id.action_set_as_ringtone -> { R.id.action_set_as_ringtone -> {
@ -193,6 +204,18 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return false 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 playerToolbar(): Toolbar?
abstract fun onShow() abstract fun onShow()
@ -237,7 +260,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
val isFavorite: Boolean = val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id) libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Main) { withContext(Main) {
val icon = if (animate) { val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else { } else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
@ -269,7 +292,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
if (PreferenceUtil.isFullScreenMode && if (PreferenceUtil.isFullScreenMode &&
view.findViewById<View>(R.id.status_bar) != null 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 = whichFragment(R.id.playerAlbumCoverFragment)
playerAlbumCoverFragment?.setCallbacks(this) playerAlbumCoverFragment?.setCallbacks(this)
@ -287,9 +310,8 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
playerToolbar()?.menu?.removeItem(R.id.action_toggle_lyrics) playerToolbar()?.menu?.removeItem(R.id.action_toggle_lyrics)
} else { } else {
playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.apply { playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.apply {
fixCheckStateOnIcon()
isCheckable = true
isChecked = PreferenceUtil.showLyrics isChecked = PreferenceUtil.showLyrics
showLyricsIcon(this)
} }
} }
requireView().setOnTouchListener( requireView().setOnTouchListener(
@ -412,15 +434,4 @@ fun goToLyrics(activity: Activity) {
null null
) )
} }
} }
/** 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.dialogs.ImportPlaylistDialog
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.dip 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.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.IScrollHelper import code.name.monkey.retromusic.interfaces.IScrollHelper
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ThemedFastScroller.create 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.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis
import me.zhanghai.android.fastscroll.FastScroller import me.zhanghai.android.fastscroll.FastScroller
import me.zhanghai.android.fastscroll.FastScrollerBuilder import me.zhanghai.android.fastscroll.FastScrollerBuilder
@ -60,7 +57,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
view.doOnPreDraw { startPostponedEnterTransition() } view.doOnPreDraw { startPostponedEnterTransition() }
enterTransition = MaterialFadeThrough().addTarget(binding.recyclerView) enterTransition = MaterialFadeThrough().addTarget(binding.recyclerView)
reenterTransition = MaterialFadeThrough().addTarget(binding.recyclerView) reenterTransition = MaterialFadeThrough().addTarget(binding.recyclerView)
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(toolbar)
mainActivity.supportActionBar?.title = null mainActivity.supportActionBar?.title = null
initLayoutManager() initLayoutManager()
initAdapter() initAdapter()
@ -99,25 +96,18 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
open fun onShuffleClicked() { open fun onShuffleClicked() {
} }
fun toolbar(): Toolbar { val toolbar: Toolbar get() = binding.appBarLayout.toolbar
return binding.toolbar
}
private fun setupToolbar() { private fun setupToolbar() {
binding.toolbar.setNavigationOnClickListener { toolbar.setNavigationOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(requireView())
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
findNavController().navigate( findNavController().navigate(
R.id.searchFragment, R.id.action_search,
null, null,
navOptions navOptions
) )
} }
val appName = resources.getString(titleRes) val appName = resources.getString(titleRes)
binding.appNameText.text = appName binding.appBarLayout.title = appName
binding.toolbarContainer.drawNextToNavbar()
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
abstract val titleRes: Int abstract val titleRes: Int
@ -155,17 +145,19 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
private fun checkIsEmpty() { private fun checkIsEmpty() {
binding.emptyText.setText(emptyMessage) 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() { private fun checkForPadding() {
val itemCount: Int = adapter?.itemCount ?: 0 val itemCount: Int = adapter?.itemCount ?: 0
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) { binding.recyclerView.updatePadding(
binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height_expanded)) bottom = if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
} else { dip(R.dimen.mini_player_height_expanded)
binding.recyclerView.updatePadding(bottom = dip(R.dimen.bottom_nav_height)) } else {
} dip(R.dimen.bottom_nav_height)
}
)
} }
private fun initLayoutManager() { private fun initLayoutManager() {
@ -209,7 +201,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu) super.onPrepareOptionsMenu(menu)
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), binding.toolbar) ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), toolbar)
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 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) inflater.inflate(R.menu.menu_main, menu)
ToolbarContentTintHelper.handleOnCreateOptionsMenu( ToolbarContentTintHelper.handleOnCreateOptionsMenu(
requireContext(), requireContext(),
binding.toolbar, toolbar,
menu, menu,
ATHToolbarActivity.getToolbarBackgroundColor(binding.toolbar) ATHToolbarActivity.getToolbarBackgroundColor(toolbar)
) )
} }

View file

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

View file

@ -21,6 +21,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -100,7 +101,7 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
private fun checkIsEmpty() { private fun checkIsEmpty() {
checkForPadding() checkForPadding()
binding.emptyEmoji.text = getEmojiByUnicode(0x1F631) 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() { private fun checkForPadding() {

View file

@ -48,7 +48,7 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
}) })
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
remove() 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.text.HtmlCompat
import androidx.core.view.doOnLayout import androidx.core.view.doOnLayout
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity 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.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.adapter.HomeAdapter 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.databinding.FragmentHomeBinding
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.drawNextToNavbar import code.name.monkey.retromusic.extensions.drawNextToNavbar
import code.name.monkey.retromusic.extensions.elevatedAccentColor 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.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.IScrollHelper import code.name.monkey.retromusic.interfaces.IScrollHelper
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.shape.MaterialShapeDrawable 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 import com.google.android.material.transition.MaterialSharedAxis
class HomeFragment : class HomeFragment :
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home), AbsMainActivityFragment(R.layout.fragment_home), IScrollHelper {
IScrollHelper {
private var _binding: HomeBindingAdapter? = null private var _binding: HomeBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = getBinding(PreferenceUtil.isHomeBanner, view) val homeBinding = FragmentHomeBinding.bind(view)
_binding = HomeBinding(homeBinding)
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(binding.toolbar)
mainActivity.supportActionBar?.title = null mainActivity.supportActionBar?.title = null
setupListeners() setupListeners()
@ -75,6 +80,9 @@ class HomeFragment :
libraryViewModel.getHome().observe(viewLifecycleOwner, { libraryViewModel.getHome().observe(viewLifecycleOwner, {
homeAdapter.swapData(it) homeAdapter.swapData(it)
}) })
libraryViewModel.getSuggestions().observe(viewLifecycleOwner, {
loadSuggestions(it)
})
loadProfile() loadProfile()
setupTitle() setupTitle()
@ -86,7 +94,7 @@ class HomeFragment :
binding.toolbar.drawNextToNavbar() binding.toolbar.drawNextToNavbar()
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
remove() remove()
mainActivity.finish() requireActivity().onBackPressed()
} }
view.doOnLayout { view.doOnLayout {
adjustPlaylistButtons() adjustPlaylistButtons()
@ -96,7 +104,7 @@ class HomeFragment :
private fun adjustPlaylistButtons() { private fun adjustPlaylistButtons() {
val buttons = val buttons =
listOf(binding.history, binding.lastAdded, binding.topPlayed, binding.actionShuffle) listOf(binding.history, binding.lastAdded, binding.topPlayed, binding.actionShuffle)
buttons.maxOf { it.lineCount }.let { maxLineCount-> buttons.maxOf { it.lineCount }.let { maxLineCount ->
buttons.forEach { button -> buttons.forEach { button ->
// Set the highest line count to every button for consistency // Set the highest line count to every button for consistency
button.setLines(maxLineCount) button.setLines(maxLineCount)
@ -150,24 +158,17 @@ class HomeFragment :
) )
) )
} }
} // Reload suggestions
binding.suggestions.refreshButton.setOnClickListener {
private fun getBinding(homeBanner: Boolean, view: View): HomeBindingAdapter { libraryViewModel.forceReload(
return if (homeBanner) { ReloadType.Suggestions
val homeBannerBinding = FragmentBannerHomeBinding.bind(view) )
HomeBindingAdapter(null, homeBannerBinding)
} else {
val homeBinding = FragmentHomeBinding.bind(view)
HomeBindingAdapter(homeBinding, null)
} }
} }
private fun setupTitle() { private fun setupTitle() {
binding.toolbar.setNavigationOnClickListener { binding.toolbar.setNavigationOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(binding.root) findNavController().navigate(R.id.action_search, null, navOptions)
reenterTransition =
MaterialSharedAxis(MaterialSharedAxis.Z, false)
findNavController().navigate(R.id.searchFragment, null, navOptions)
} }
val hexColor = String.format("#%06X", 0xFFFFFF and accentColor()) val hexColor = String.format("#%06X", 0xFFFFFF and accentColor())
val appName = HtmlCompat.fromHtml( val appName = HtmlCompat.fromHtml(
@ -234,6 +235,51 @@ class HomeFragment :
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false) 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 { companion object {
const val TAG: String = "BannerHomeFragment" const val TAG: String = "BannerHomeFragment"
@ -270,7 +316,7 @@ class HomeFragment :
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
libraryViewModel.fetchHomeSections() libraryViewModel.forceReload(ReloadType.HomeSections)
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

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

View file

@ -12,8 +12,10 @@ import androidx.preference.PreferenceManager
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.SHOW_LYRICS import code.name.monkey.retromusic.SHOW_LYRICS
import code.name.monkey.retromusic.databinding.FragmentCoverLyricsBinding 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.AbsMusicServiceFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToLyrics
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
@ -45,6 +47,14 @@ class CoverLyricsFragment : AbsMusicServiceFragment(R.layout.fragment_cover_lyri
if (PreferenceUtil.showLyrics) { if (PreferenceUtil.showLyrics) {
progressViewUpdateHelper?.start() 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?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
@ -110,7 +120,7 @@ class CoverLyricsFragment : AbsMusicServiceFragment(R.layout.fragment_cover_lyri
if (lyrics !is AbsSynchronizedLyrics) return if (lyrics !is AbsSynchronizedLyrics) return
val synchronizedLyrics = lyrics as AbsSynchronizedLyrics val synchronizedLyrics = lyrics as AbsSynchronizedLyrics
lyricsLayout.visibility = View.VISIBLE lyricsLayout.isVisible = true
lyricsLayout.alpha = 1f lyricsLayout.alpha = 1f
val oldLine = lyricsLine2.text.toString() val oldLine = lyricsLine2.text.toString()

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.other
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.activity.addCallback
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.navigation.fragment.FragmentNavigatorExtras 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.databinding.FragmentPlaylistDetailBinding
import code.name.monkey.retromusic.db.toSong import code.name.monkey.retromusic.db.toSong
import code.name.monkey.retromusic.extensions.dipToPix 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.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.interfaces.IAlbumClickListener import code.name.monkey.retromusic.interfaces.IAlbumClickListener
import code.name.monkey.retromusic.interfaces.IArtistClickListener 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.Album
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil 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.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail), class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
IArtistClickListener, IAlbumClickListener { IArtistClickListener, IAlbumClickListener, ICabHolder {
private val args by navArgs<DetailListFragmentArgs>() private val args by navArgs<DetailListFragmentArgs>()
private var _binding: FragmentPlaylistDetailBinding? = null private var _binding: FragmentPlaylistDetailBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -88,6 +97,12 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
MaterialShapeDrawable.createWithElevationOverlay(requireContext()) MaterialShapeDrawable.createWithElevationOverlay(requireContext())
postponeEnterTransition() postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() } view.doOnPreDraw { startPostponedEnterTransition() }
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) {
remove()
findNavController().navigateUp()
}
}
} }
private fun lastAddedSongs() { private fun lastAddedSongs() {
@ -95,16 +110,16 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = ShuffleButtonSongAdapter( val songAdapter = ShuffleButtonSongAdapter(
requireActivity(), requireActivity(),
mutableListOf(), mutableListOf(),
R.layout.item_list, null R.layout.item_list, this
) )
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = songAdapter adapter = songAdapter
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
scheduleLayoutAnimation() scheduleLayoutAnimation()
} }
libraryViewModel.recentSongs().observe(viewLifecycleOwner, { songs -> libraryViewModel.recentSongs().observe(viewLifecycleOwner) { songs ->
songAdapter.swapDataSet(songs) songAdapter.swapDataSet(songs)
}) }
} }
private fun topPlayed() { private fun topPlayed() {
@ -112,15 +127,15 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = ShuffleButtonSongAdapter( val songAdapter = ShuffleButtonSongAdapter(
requireActivity(), requireActivity(),
mutableListOf(), mutableListOf(),
R.layout.item_list, null R.layout.item_list, this
) )
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = songAdapter adapter = songAdapter
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
} }
libraryViewModel.playCountSongs().observe(viewLifecycleOwner, { songs -> libraryViewModel.playCountSongs().observe(viewLifecycleOwner) { songs ->
songAdapter.swapDataSet(songs) songAdapter.swapDataSet(songs)
}) }
} }
private fun loadHistory() { private fun loadHistory() {
@ -129,15 +144,15 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = ShuffleButtonSongAdapter( val songAdapter = ShuffleButtonSongAdapter(
requireActivity(), requireActivity(),
mutableListOf(), mutableListOf(),
R.layout.item_list, null R.layout.item_list, this
) )
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = songAdapter adapter = songAdapter
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
} }
libraryViewModel.observableHistorySongs().observe(viewLifecycleOwner, { libraryViewModel.observableHistorySongs().observe(viewLifecycleOwner) {
songAdapter.swapDataSet(it) songAdapter.swapDataSet(it)
}) }
} }
private fun loadFavorite() { private fun loadFavorite() {
@ -145,50 +160,55 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
val songAdapter = SongAdapter( val songAdapter = SongAdapter(
requireActivity(), requireActivity(),
mutableListOf(), mutableListOf(),
R.layout.item_list, null R.layout.item_list, this
) )
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = songAdapter adapter = songAdapter
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
} }
libraryViewModel.favorites().observe(viewLifecycleOwner, { songEntities -> libraryViewModel.favorites().observe(viewLifecycleOwner) { songEntities ->
val songs = songEntities.map { songEntity -> songEntity.toSong() } val songs = songEntities.map { songEntity -> songEntity.toSong() }
songAdapter.swapDataSet(songs) songAdapter.swapDataSet(songs)
}) }
} }
private fun loadArtists(title: Int, type: Int) { private fun loadArtists(title: Int, type: Int) {
binding.toolbar.setTitle(title) binding.toolbar.setTitle(title)
libraryViewModel.artists(type).observe(viewLifecycleOwner, { artists -> val artistAdapter = artistAdapter(listOf())
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = artistAdapter(artists) adapter = artistAdapter
layoutManager = gridLayoutManager() layoutManager = gridLayoutManager()
} }
}) libraryViewModel.artists(type).observe(viewLifecycleOwner) { artists ->
artistAdapter.swapDataSet(artists)
}
} }
private fun loadAlbums(title: Int, type: Int) { private fun loadAlbums(title: Int, type: Int) {
binding.toolbar.setTitle(title) binding.toolbar.setTitle(title)
libraryViewModel.albums(type).observe(viewLifecycleOwner, { albums -> val albumAdapter = albumAdapter(listOf())
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = albumAdapter(albums) adapter = albumAdapter
layoutManager = gridLayoutManager() layoutManager = gridLayoutManager()
} }
}) libraryViewModel.albums(type).observe(viewLifecycleOwner) { albums ->
albumAdapter.swapDataSet(albums)
}
} }
private fun artistAdapter(artists: List<Artist>): ArtistAdapter = ArtistAdapter( private fun artistAdapter(artists: List<Artist>): ArtistAdapter = ArtistAdapter(
requireActivity(), requireActivity(),
artists, artists,
R.layout.item_grid_circle, R.layout.item_grid_circle,
null, this@DetailListFragment this, this@DetailListFragment
) )
private fun albumAdapter(albums: List<Album>): AlbumAdapter = AlbumAdapter( private fun albumAdapter(albums: List<Album>): AlbumAdapter = AlbumAdapter(
requireActivity(), requireActivity(),
albums, albums,
R.layout.item_grid, R.layout.item_grid,
null, this@DetailListFragment this, this@DetailListFragment
) )
private fun linearLayoutManager(): LinearLayoutManager = private fun linearLayoutManager(): LinearLayoutManager =
@ -228,4 +248,37 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
super.onDestroyView() super.onDestroyView()
_binding = null _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 package code.name.monkey.retromusic.fragments.other
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore
import android.text.InputType import android.text.InputType
import android.view.* 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.ViewCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment 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.FragmentNormalLyricsBinding
import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
import code.name.monkey.retromusic.extensions.accentColor 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.textColorSecondary
import code.name.monkey.retromusic.extensions.uri
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.lyrics.LrcView import code.name.monkey.retromusic.lyrics.LrcView
import code.name.monkey.retromusic.model.AudioTagInfo import code.name.monkey.retromusic.model.AudioTagInfo
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.LyricUtil import code.name.monkey.retromusic.util.*
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialdialogs.LayoutMode
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.input.input import com.afollestad.materialdialogs.input.input
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
@ -55,6 +59,7 @@ import kotlinx.coroutines.*
import org.jaudiotagger.audio.AudioFileIO import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.util.* import java.util.*
class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) { class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
@ -95,6 +100,43 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
return transform 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true) setHasOptionsMenu(true)
@ -105,15 +147,9 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
ViewCompat.setTransitionName(binding.container, "lyrics") ViewCompat.setTransitionName(binding.container, "lyrics")
setupWakelock() setupWakelock()
binding.tabLyrics.setBackgroundColor(surfaceColor())
binding.container.setBackgroundColor(surfaceColor())
setupViews() setupViews()
setupToolbar() setupToolbar()
updateTitleSong() updateTitleSong()
if (VersionUtils.hasR()) {
binding.editButton.isVisible = false
}
} }
private fun setupViews() { private fun setupViews() {
@ -144,7 +180,6 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
private fun setupToolbar() { private fun setupToolbar() {
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(binding.toolbar)
binding.toolbar.setBackgroundColor(surfaceColor())
ToolbarContentTintHelper.colorBackButton(binding.toolbar) ToolbarContentTintHelper.colorBackButton(binding.toolbar)
binding.toolbar.setNavigationOnClickListener { binding.toolbar.setNavigationOnClickListener {
findNavController().navigateUp() findNavController().navigateUp()
@ -204,7 +239,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
e.printStackTrace() e.printStackTrace()
} }
MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show { MaterialDialog(requireContext()).show {
title(res = R.string.edit_normal_lyrics) title(res = R.string.edit_normal_lyrics)
input( input(
hintRes = R.string.paste_lyrics_here, hintRes = R.string.paste_lyrics_here,
@ -213,12 +248,30 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
) { _, input -> ) { _, input ->
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java) val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString() fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
syncedLyrics = input.toString()
GlobalScope.launch { GlobalScope.launch {
TagWriter.writeTagsToFiles( if (VersionUtils.hasR()) {
requireContext(), AudioTagInfo( cacheFile = TagWriter.writeTagsToFilesR(
listOf(song.data), fieldKeyValueMap, null 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
)
)
}
} }
} }
positiveButton(res = R.string.save) { positiveButton(res = R.string.save) {
@ -233,14 +286,40 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
private fun editSyncedLyrics() { private fun editSyncedLyrics() {
val content: String = LyricUtil.getStringFromLrc(LyricUtil.getSyncedLyricsFile(song)) 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) title(res = R.string.edit_synced_lyrics)
input( input(
hintRes = R.string.paste_timeframe_lyrics_here, hintRes = R.string.paste_timeframe_lyrics_here,
prefill = content, prefill = content,
inputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_CLASS_TEXT inputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_CLASS_TEXT
) { _, input -> ) { _, input ->
LyricUtil.writeLrc(song, input.toString()) 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) { positiveButton(res = R.string.save) {
(lyricsSectionsAdapter.fragments[0].first as SyncedLyrics).loadLRCLyrics() (lyricsSectionsAdapter.fragments[0].first as SyncedLyrics).loadLRCLyrics()
@ -285,11 +364,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
if (lyrics.isNullOrEmpty()) { binding.noLyricsFound.isVisible = lyrics.isNullOrEmpty()
binding.noLyricsFound.visibility = View.VISIBLE
} else {
binding.noLyricsFound.visibility = View.GONE
}
binding.normalLyrics.text = lyrics binding.normalLyrics.text = lyrics
} }

View file

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

View file

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

View file

@ -15,13 +15,12 @@
package code.name.monkey.retromusic.fragments.player.adaptive package code.name.monkey.retromusic.fragments.player.adaptive
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil 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.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -43,38 +38,35 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class AdaptivePlaybackControlsFragment : class AdaptivePlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_adaptive_player_playback_controls) { 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 var _binding: FragmentAdaptivePlayerPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) { override val progressSlider: SeekBar
super.onCreate(savedInstanceState) get() = binding.progressSlider
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onCreateView( override val shuffleButton: ImageButton
inflater: LayoutInflater, get() = binding.shuffleButton
container: ViewGroup?,
savedInstanceState: Bundle? override val repeatButton: ImageButton
): View { get() = binding.repeatButton
_binding = FragmentAdaptivePlayerPlaybackControlsBinding.inflate(inflater, container, false)
return binding.root 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setUpMusicControllers() _binding = FragmentAdaptivePlayerPlaybackControlsBinding.bind(view)
binding.playPauseButton.setOnClickListener { setUpPlayPauseFab()
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.playPauseButton)
}
} }
private fun updateSong() { 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() { override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged() super.onPlayingMetaChanged()
updateSong() updateSong()
@ -163,7 +145,14 @@ class AdaptivePlaybackControlsFragment :
} }
private fun setUpPlayPauseFab() { private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
} }
private fun updatePlayPauseDrawableState() { private fun updatePlayPauseDrawableState() {
@ -174,103 +163,9 @@ class AdaptivePlaybackControlsFragment :
} }
} }
private fun setUpMusicControllers() { override fun show() {}
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() { override fun hide() {}
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 onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()

View file

@ -16,13 +16,13 @@ package code.name.monkey.retromusic.fragments.player.blur
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -48,28 +44,32 @@ class BlurPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_blur_player_playback_controls) { AbsPlayerControlsFragment(R.layout.fragment_blur_player_playback_controls) {
private var _binding: FragmentBlurPlayerPlaybackControlsBinding? = null private var _binding: FragmentBlurPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!! 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?) { override val progressSlider: SeekBar
super.onCreate(savedInstanceState) get() = binding.progressSlider
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentBlurPlayerPlaybackControlsBinding.bind(view) _binding = FragmentBlurPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation()
}
binding.title.isSelected = true binding.title.isSelected = true
binding.text.isSelected = true binding.text.isSelected = true
binding.title.setOnClickListener { 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() { override fun onServiceConnected() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updateRepeatState() updateRepeatState()
@ -159,7 +149,14 @@ class BlurPlaybackControlsFragment :
} }
private fun setUpPlayPauseFab() { private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
} }
private fun updatePlayPauseDrawableState() { 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() { public override fun show() {
binding.playPauseButton.animate() binding.playPauseButton.animate()
.scaleX(1f) .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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null

View file

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

View file

@ -19,6 +19,7 @@ import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -46,31 +43,29 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class CardPlaybackControlsFragment : class CardPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_card_player_playback_controls) { 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 var _binding: FragmentCardPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) { override val shuffleButton: ImageButton
super.onCreate(savedInstanceState) get() = binding.mediaButton.shuffleButton
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
} 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentCardPlayerPlaybackControlsBinding.bind(view) _binding = FragmentCardPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
binding.mediaButton.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.mediaButton.playPauseButton)
}
binding.title.isSelected = true binding.title.isSelected = true
binding.text.isSelected = true binding.text.isSelected = true
binding.title.setOnClickListener { 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() { override fun onServiceConnected() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updateRepeatState() updateRepeatState()
@ -167,7 +152,14 @@ class CardPlaybackControlsFragment :
} }
private fun setUpPlayPauseFab() { 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() { 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() { private fun updateProgressTextColor() {
val color = MaterialValueHelper.getPrimaryTextColor(context, false) val color = MaterialValueHelper.getPrimaryTextColor(context, false)
binding.songTotalTime.setTextColor(color) binding.songTotalTime.setTextColor(color)
binding.songCurrentProgress.setTextColor(color) binding.songCurrentProgress.setTextColor(color)
} }
public override fun show() { 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 hide() {}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()

View file

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

View file

@ -16,11 +16,11 @@ package code.name.monkey.retromusic.fragments.player.cardblur
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.TintHelper 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.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler 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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -43,23 +40,29 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class CardBlurPlaybackControlsFragment : class CardBlurPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_card_blur_player_playback_controls) { 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 var _binding: FragmentCardBlurPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) { override val shuffleButton: ImageButton
super.onCreate(savedInstanceState) get() = binding.mediaButton.shuffleButton
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
} 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentCardBlurPlayerPlaybackControlsBinding.bind(view) _binding = FragmentCardBlurPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
binding.progressSlider.applyColor(Color.WHITE)
} }
override fun setColor(color: MediaNotificationProcessor) { override fun setColor(color: MediaNotificationProcessor) {
@ -96,16 +99,6 @@ class CardBlurPlaybackControlsFragment :
binding.songInfo.setTextColor(color) binding.songInfo.setTextColor(color)
} }
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onServiceConnected() { override fun onServiceConnected() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updateRepeatState() updateRepeatState()
@ -139,78 +132,6 @@ class CardBlurPlaybackControlsFragment :
updateShuffleState() 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() { public override fun show() {
binding.mediaButton.playPauseButton.animate() binding.mediaButton.playPauseButton.animate()
.scaleX(1f) .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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null

View file

@ -15,6 +15,7 @@
package code.name.monkey.retromusic.fragments.player.circle package code.name.monkey.retromusic.fragments.player.circle
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff import android.graphics.PorterDuff
@ -22,9 +23,7 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.media.AudioManager import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.SeekBar 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.R
import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding
import code.name.monkey.retromusic.extensions.* 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.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
@ -77,17 +77,10 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentCirclePlayerBinding.bind(view)
setupViews() setupViews()
binding.title.isSelected = true binding.title.isSelected = true
binding.title.setOnClickListener { binding.title.setOnClickListener {
@ -132,10 +125,11 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
) )
} }
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() { private fun setUpPrevNext() {
updatePrevNextColor() updatePrevNextColor()
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() } binding.nextButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), true))
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() } binding.previousButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), false))
} }
private fun updatePrevNextColor() { 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.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.surfaceColor 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.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
@ -455,10 +456,21 @@ class ClassicPlayerFragment : AbsPlayerFragment(R.layout.fragment_classic_player
setUpProgressSlider() setUpProgressSlider()
} }
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() { private fun setUpPrevNext() {
updatePrevNextColor() updatePrevNextColor()
binding.playerControlsContainer.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() } binding.playerControlsContainer.nextButton.setOnTouchListener(
binding.playerControlsContainer.previousButton.setOnClickListener { MusicPlayerRemote.back() } MusicSeekSkipTouchListener(
requireActivity(),
true
)
)
binding.playerControlsContainer.previousButton.setOnTouchListener(
MusicSeekSkipTouchListener(
requireActivity(),
false
)
)
} }
private fun updatePrevNextColor() { private fun updatePrevNextColor() {

View file

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

View file

@ -17,16 +17,15 @@ package code.name.monkey.retromusic.fragments.player.color
import android.animation.Animator import android.animation.Animator
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewAnimationUtils import android.view.ViewAnimationUtils
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlin.math.sqrt import kotlin.math.sqrt
class ColorPlaybackControlsFragment : 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 var _binding: FragmentColorPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) { override val shuffleButton: ImageButton
super.onCreate(savedInstanceState) get() = binding.shuffleButton
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onCreateView( override val repeatButton: ImageButton
inflater: LayoutInflater, get() = binding.repeatButton
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentColorPlayerPlaybackControlsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onResume() { override val nextButton: ImageButton
super.onResume() get() = binding.nextButton
progressViewUpdateHelper.start()
}
override fun onPause() { override val previousButton: ImageButton
super.onPause() get() = binding.previousButton
progressViewUpdateHelper.stop()
} override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setUpMusicControllers() _binding = FragmentColorPlayerPlaybackControlsBinding.bind(view)
setUpPlayPauseFab()
binding.title.isSelected = true binding.title.isSelected = true
binding.text.isSelected = true binding.text.isSelected = true
binding.title.setOnClickListener { binding.title.setOnClickListener {
@ -155,80 +145,23 @@ class ColorPlaybackControlsFragment :
private fun setUpPlayPauseFab() { private fun setUpPlayPauseFab() {
TintHelper.setTintAuto(binding.playPauseButton, Color.WHITE, true) TintHelper.setTintAuto(binding.playPauseButton, Color.WHITE, true)
TintHelper.setTintAuto(binding.playPauseButton, Color.BLACK, false) 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() { private fun updatePlayPauseDrawableState() {
when { binding.playPauseButton.setImageResource(
MusicPlayerRemote.isPlaying -> binding.playPauseButton.setImageResource(R.drawable.ic_pause) when {
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() { 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 { fun createRevealAnimator(view: View): Animator {
val location = IntArray(2) val location = IntArray(2)
binding.playPauseButton.getLocationOnScreen(location) binding.playPauseButton.getLocationOnScreen(location)

View file

@ -15,13 +15,13 @@
package code.name.monkey.retromusic.fragments.player.fit package code.name.monkey.retromusic.fragments.player.fit
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class FitPlaybackControlsFragment : class FitPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_fit_playback_controls) { AbsPlayerControlsFragment(R.layout.fragment_fit_playback_controls) {
private var _binding: FragmentFitPlaybackControlsBinding? = null private var _binding: FragmentFitPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
private var lastPlaybackControlsColor: Int = 0 override val shuffleButton: ImageButton
private var lastDisabledPlaybackControlsColor: Int = 0 get() = binding.shuffleButton
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
override fun onCreate(savedInstanceState: Bundle?) { override val repeatButton: ImageButton
super.onCreate(savedInstanceState) get() = binding.repeatButton
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
} 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentFitPlaybackControlsBinding.bind(view) _binding = FragmentFitPlaybackControlsBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
binding.title.isSelected = true binding.title.isSelected = true
binding.text.isSelected = true binding.text.isSelected = true
@ -70,15 +78,6 @@ class FitPlaybackControlsFragment :
binding.text.setOnClickListener { binding.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation()
}
} }
private fun updateSong() { 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() { override fun onServiceConnected() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updateRepeatState() updateRepeatState()
@ -163,7 +152,14 @@ class FitPlaybackControlsFragment :
} }
private fun setUpPlayPauseFab() { private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
} }
private fun updatePlayPauseDrawableState() { 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() { public override fun show() {
binding.playPauseButton.animate() binding.playPauseButton.animate()
.scaleX(1f) .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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null

View file

@ -15,11 +15,11 @@
package code.name.monkey.retromusic.fragments.player.flat package code.name.monkey.retromusic.fragments.player.flat
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler 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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -45,22 +42,28 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class FlatPlaybackControlsFragment : class FlatPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_flat_player_playback_controls), Callback { 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 var _binding: FragmentFlatPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) { override val shuffleButton: ImageButton
super.onCreate(savedInstanceState) get() = binding.shuffleButton
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
} 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentFlatPlayerPlaybackControlsBinding.bind(view) _binding = FragmentFlatPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers() binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
binding.title.isSelected = true binding.title.isSelected = true
binding.text.isSelected = true binding.text.isSelected = true
binding.title.setOnClickListener { 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() { public override fun show() {
binding.playPauseButton.animate() binding.playPauseButton.animate()
.scaleX(1f) .scaleX(1f)
@ -157,10 +150,6 @@ class FlatPlaybackControlsFragment :
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
} }
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
}
private fun updatePlayPauseDrawableState() { private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) { if (MusicPlayerRemote.isPlaying) {
binding.playPauseButton.setImageResource(R.drawable.ic_pause) binding.playPauseButton.setImageResource(R.drawable.ic_pause)
@ -169,13 +158,6 @@ class FlatPlaybackControlsFragment :
} }
} }
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun updateSong() { private fun updateSong() {
val song = MusicPlayerRemote.currentSong val song = MusicPlayerRemote.currentSong
binding.title.text = song.title binding.title.text = song.title
@ -196,81 +178,8 @@ class FlatPlaybackControlsFragment :
updateShuffleState() 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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
} }

View file

@ -18,7 +18,6 @@ import android.animation.ObjectAnimator
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
@ -26,10 +25,13 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
@ -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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler 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.model.Song
import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
@ -66,17 +66,30 @@ class FullPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_full_player_controls), AbsPlayerControlsFragment(R.layout.fragment_full_player_controls),
PopupMenu.OnMenuItemClickListener { PopupMenu.OnMenuItemClickListener {
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private val libraryViewModel: LibraryViewModel by sharedViewModel() private val libraryViewModel: LibraryViewModel by sharedViewModel()
private var _binding: FragmentFullPlayerControlsBinding? = null private var _binding: FragmentFullPlayerControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) { override val progressSlider: SeekBar
super.onCreate(savedInstanceState) get() = binding.progressSlider
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) 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() { public override fun show() {
binding.playPauseButton.animate() binding.playPauseButton.animate()
.scaleX(1f) .scaleX(1f)
@ -190,10 +193,6 @@ class FullPlaybackControlsFragment :
private fun setUpMusicControllers() { private fun setUpMusicControllers() {
setUpPlayPauseFab() setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
setupFavourite() setupFavourite()
setupMenu() setupMenu()
} }
@ -213,43 +212,6 @@ class FullPlaybackControlsFragment :
return (parentFragment as FullPlayerFragment).onMenuItemClick(item!!) 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() { override fun onRepeatModeChanged() {
updateRepeatState() updateRepeatState()
} }
@ -258,53 +220,6 @@ class FullPlaybackControlsFragment :
updateShuffleState() 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() { private fun setupFavourite() {
binding.songFavourite.setOnClickListener { binding.songFavourite.setOnClickListener {
toggleFavorite(MusicPlayerRemote.currentSong) toggleFavorite(MusicPlayerRemote.currentSong)
@ -320,7 +235,7 @@ class FullPlaybackControlsFragment :
val isFavorite: Boolean = val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id) libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val icon = if (animate) { val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else { } else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border

View file

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

View file

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

View file

@ -15,12 +15,13 @@
package code.name.monkey.retromusic.fragments.player.lockscreen package code.name.monkey.retromusic.fragments.player.lockscreen
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper 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.extensions.textColorSecondary
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler 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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -46,23 +44,34 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class LockScreenControlsFragment : class LockScreenControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_lock_screen_playback_controls) { 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 var _binding: FragmentLockScreenPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) { override val shuffleButton: ImageButton
super.onCreate(savedInstanceState) get() = binding.shuffleButton
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentLockScreenPlaybackControlsBinding.bind(view) _binding = FragmentLockScreenPlaybackControlsBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
binding.title.isSelected = true binding.title.isSelected = true
} }
@ -72,16 +81,6 @@ class LockScreenControlsFragment :
binding.text.text = String.format("%s - %s", song.artistName, song.albumName) 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() { override fun onServiceConnected() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updateRepeatState() 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() { public override fun show() {
binding.playPauseButton.animate() binding.playPauseButton.animate()
.scaleX(1f) .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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null

View file

@ -19,7 +19,9 @@ import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler 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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -44,22 +43,35 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class MaterialControlsFragment : class MaterialControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_material_playback_controls) { 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 var _binding: FragmentMaterialPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override fun onCreate(savedInstanceState: Bundle?) { override val shuffleButton: ImageButton
super.onCreate(savedInstanceState) get() = binding.shuffleButton
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentMaterialPlaybackControlsBinding.bind(view) _binding = FragmentMaterialPlaybackControlsBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
binding.title.isSelected = true binding.title.isSelected = true
binding.text.isSelected = true binding.text.isSelected = true
binding.title.setOnClickListener { 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() { override fun onServiceConnected() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
updateRepeatState() updateRepeatState()
@ -175,103 +177,9 @@ class MaterialControlsFragment :
} }
} }
private fun setUpMusicControllers() { public override fun show() {}
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
}
private fun setUpPrevNext() { public override fun hide() {}
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())
}
override fun onDestroyView() { override fun onDestroyView() {
super.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.databinding.FragmentPlayerBinding
import code.name.monkey.retromusic.extensions.colorControlNormal import code.name.monkey.retromusic.extensions.colorControlNormal
import code.name.monkey.retromusic.extensions.drawAboveSystemBars 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.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
@ -167,7 +168,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player),
} }
private fun startOrStopSnow(isSnowFalling: Boolean) { private fun startOrStopSnow(isSnowFalling: Boolean) {
if (isSnowFalling) { if (isSnowFalling && !surfaceColor().isColorLight) {
binding.snowfallView.isVisible = true binding.snowfallView.isVisible = true
binding.snowfallView.restartFalling() binding.snowfallView.restartFalling()
} else { } else {

View file

@ -14,13 +14,12 @@
*/ */
package code.name.monkey.retromusic.fragments.player.normal package code.name.monkey.retromusic.fragments.player.normal
import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.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.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class PlayerPlaybackControlsFragment : class PlayerPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_player_playback_controls) { 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 var _binding: FragmentPlayerPlaybackControlsBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) { override val progressSlider: SeekBar
super.onCreate(savedInstanceState) get() = binding.progressSlider
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentPlayerPlaybackControlsBinding.bind(view) _binding = FragmentPlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers()
binding.playPauseButton.setOnClickListener { setUpPlayPauseFab()
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.playPauseButton)
}
binding.title.isSelected = true binding.title.isSelected = true
binding.text.isSelected = true binding.text.isSelected = true
binding.title.setOnClickListener { 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() { override fun onServiceConnected() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
@ -161,7 +152,14 @@ class PlayerPlaybackControlsFragment :
} }
private fun setUpPlayPauseFab() { private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
} }
private fun updatePlayPauseDrawableState() { 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() { public override fun show() {
binding.playPauseButton.animate() binding.playPauseButton.animate()
.scaleX(1f) .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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null

View file

@ -20,7 +20,9 @@ import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper 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.extensions.applyColor
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler 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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -44,26 +43,29 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class PeakPlayerControlFragment : AbsPlayerControlsFragment(R.layout.fragment_peak_control_player) { 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 var _binding: FragmentPeakControlPlayerBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) { override val progressSlider: SeekBar
super.onCreate(savedInstanceState) get() = binding.progressSlider
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onResume() { override val shuffleButton: ImageButton
super.onResume() get() = binding.shuffleButton
progressViewUpdateHelper.start()
}
override fun onPause() { override val repeatButton: ImageButton
super.onPause() get() = binding.repeatButton
progressViewUpdateHelper.stop()
} 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( override fun onViewCreated(
view: View, view: View,
@ -71,14 +73,12 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment(R.layout.fragment_pe
) { ) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentPeakControlPlayerBinding.bind(view) _binding = FragmentPeakControlPlayerBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
} }
override fun show() { override fun show() {}
}
override fun hide() { override fun hide() {}
}
override fun setColor(color: MediaNotificationProcessor) { override fun setColor(color: MediaNotificationProcessor) {
val controlsColor = 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() { private fun setUpPlayPauseFab() {
TintHelper.setTintAuto(binding.playPauseButton, Color.WHITE, true) TintHelper.setTintAuto(binding.playPauseButton, Color.WHITE, true)
TintHelper.setTintAuto(binding.playPauseButton, Color.BLACK, false) TintHelper.setTintAuto(binding.playPauseButton, Color.BLACK, false)
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) 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() { override fun onPlayStateChanged() {
super.onPlayStateChanged() super.onPlayStateChanged()
updatePlayPauseDrawableState() updatePlayPauseDrawableState()

View file

@ -15,13 +15,13 @@
package code.name.monkey.retromusic.fragments.player.plain package code.name.monkey.retromusic.fragments.player.plain
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil 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.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.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.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -50,12 +46,30 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class PlainPlaybackControlsFragment : class PlainPlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_plain_controls_fragment) { 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 var _binding: FragmentPlainControlsFragmentBinding? = null
private val binding get() = _binding!! 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() { override fun onPlayStateChanged() {
updatePlayPauseDrawableState() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentPlainControlsFragmentBinding.bind(view) _binding = FragmentPlainControlsFragmentBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener { binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) { if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong() MusicPlayerRemote.pauseSong()
} else { } else {
MusicPlayerRemote.resumePlaying() 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) { override fun setColor(color: MediaNotificationProcessor) {
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground) val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
if (ColorUtil.isColorLight(colorBg)) { if (ColorUtil.isColorLight(colorBg)) {
@ -179,53 +157,6 @@ class PlainPlaybackControlsFragment :
updatePrevNextColor() 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() { public override fun show() {
binding.playPauseButton.animate() binding.playPauseButton.animate()
.scaleX(1f) .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() { private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) { if (MusicPlayerRemote.isPlaying) {
binding.playPauseButton.setImageResource(R.drawable.ic_pause) 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() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null

View file

@ -14,10 +14,10 @@
*/ */
package code.name.monkey.retromusic.fragments.player.simple package code.name.monkey.retromusic.fragments.player.simple
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.widget.ImageButton
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil 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.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -49,10 +46,17 @@ class SimplePlaybackControlsFragment :
private var _binding: FragmentSimpleControlsFragmentBinding? = null private var _binding: FragmentSimpleControlsFragmentBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
override val shuffleButton: ImageButton
get() = binding.shuffleButton
private var lastPlaybackControlsColor: Int = 0 override val repeatButton: ImageButton
private var lastDisabledPlaybackControlsColor: Int = 0 get() = binding.repeatButton
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override fun onPlayStateChanged() { override fun onPlayStateChanged() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
@ -73,37 +77,15 @@ class SimplePlaybackControlsFragment :
updateSong() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentSimpleControlsFragmentBinding.bind(view) _binding = FragmentSimpleControlsFragmentBinding.bind(view)
setUpMusicControllers() setUpPlayPauseFab()
binding.title.isSelected = true binding.title.isSelected = true
binding.text.setOnClickListener { binding.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
showBounceAnimation(binding.playPauseButton)
}
binding.title.setOnClickListener { binding.title.setOnClickListener {
goToAlbum(requireActivity()) 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() { private fun updateSong() {
val song = MusicPlayerRemote.currentSong val song = MusicPlayerRemote.currentSong
binding.title.text = song.title binding.title.text = song.title
@ -213,9 +129,6 @@ class SimplePlaybackControlsFragment :
} }
} }
override fun setUpProgressSlider() {
}
override fun onUpdateProgressViews(progress: Int, total: Int) { override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.songCurrentProgress.text = String.format( binding.songCurrentProgress.text = String.format(
"%s / %s", "%s / %s",
@ -263,7 +176,14 @@ class SimplePlaybackControlsFragment :
} }
private fun setUpPlayPauseFab() { private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
it.showBounceAnimation()
}
} }
private fun updatePlayPauseDrawableState() { private fun updatePlayPauseDrawableState() {

View file

@ -14,15 +14,13 @@
*/ */
package code.name.monkey.retromusic.fragments.player.tiny package code.name.monkey.retromusic.fragments.player.tiny
import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.ImageButton
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentTinyControlsFragmentBinding import code.name.monkey.retromusic.databinding.FragmentTinyControlsFragmentBinding
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment 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 import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class TinyPlaybackControlsFragment : class TinyPlaybackControlsFragment :
@ -30,14 +28,15 @@ class TinyPlaybackControlsFragment :
private var _binding: FragmentTinyControlsFragmentBinding? = null private var _binding: FragmentTinyControlsFragmentBinding? = null
private val binding get() = _binding!! 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) { override fun setColor(color: MediaNotificationProcessor) {
lastPlaybackControlsColor = color.secondaryTextColor lastPlaybackControlsColor = color.secondaryTextColor
@ -50,66 +49,9 @@ class TinyPlaybackControlsFragment :
override fun onUpdateProgressViews(progress: Int, total: Int) { override fun onUpdateProgressViews(progress: Int, total: Int) {
} }
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentTinyControlsFragmentBinding.bind(view) _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() { override fun onServiceConnected() {

View file

@ -59,11 +59,9 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player),
return binding.playerToolbar return binding.playerToolbar
} }
override fun onShow() { override fun onShow() {}
}
override fun onHide() { override fun onHide() {}
}
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
return false 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.db.toSongs
import code.name.monkey.retromusic.extensions.dip import code.name.monkey.retromusic.extensions.dip
import code.name.monkey.retromusic.extensions.surfaceColor 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.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
@ -149,8 +150,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
} }
private fun showEmptyView() { private fun showEmptyView() {
binding.empty.visibility = View.VISIBLE binding.empty.isVisible = true
binding.emptyText.visibility = View.VISIBLE binding.emptyText.isVisible = true
} }
fun songs(songs: List<Song>) { fun songs(songs: List<Song>) {

View file

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

View file

@ -130,7 +130,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
} }
private fun updateCurrentSong() { private fun updateCurrentSong() {
binding.toolbar.subtitle = getUpNextAndQueueTime() binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
} }
override fun onPlayingMetaChanged() { override fun onPlayingMetaChanged() {
@ -140,7 +140,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
private fun updateQueuePosition() { private fun updateQueuePosition() {
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position) playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
resetToCurrentPosition() resetToCurrentPosition()
binding.toolbar.subtitle = getUpNextAndQueueTime() binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
} }
private fun updateQueue() { private fun updateQueue() {
@ -179,7 +179,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
} }
private fun setupToolbar() { private fun setupToolbar() {
binding.toolbar.subtitle = getUpNextAndQueueTime() binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor()) binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
ColorStateList.valueOf( ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor( MaterialValueHelper.getPrimaryTextColor(
@ -190,10 +190,13 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
binding.clearQueue.setTextColor(this) binding.clearQueue.setTextColor(this)
binding.clearQueue.iconTint = this binding.clearQueue.iconTint = this
} }
binding.toolbar.apply { binding.appBarLayout.pinWhenScrolled()
binding.appBarLayout.toolbar.apply {
setNavigationOnClickListener { setNavigationOnClickListener {
findNavController().navigateUp() findNavController().navigateUp()
} }
setTitle(R.string.now_playing_queue)
setTitleTextAppearance(context, R.style.ToolbarTextAppearanceNormal)
setNavigationIcon(R.drawable.ic_keyboard_backspace_black) setNavigationIcon(R.drawable.ic_keyboard_backspace_black)
ToolbarContentTintHelper.colorBackButton(this) 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.chip.ChipGroup
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.textfield.TextInputEditText 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 net.yslibrary.android.keyboardvisibilityevent.KeyboardVisibilityEvent
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -63,8 +63,8 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(view) enterTransition = MaterialFadeThrough().addTarget(view)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) reenterTransition = MaterialFadeThrough().addTarget(view)
_binding = FragmentSearchBinding.bind(view) _binding = FragmentSearchBinding.bind(view)
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(binding.toolbar)
libraryViewModel.clearSearchResult() libraryViewModel.clearSearchResult()
@ -147,7 +147,7 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
super.onChanged() super.onChanged()
binding.empty.isVisible = searchAdapter.itemCount < 1 binding.empty.isVisible = searchAdapter.itemCount < 1
val height = dipToPix(52f) val height = dipToPix(52f)
binding.recyclerView.setPadding(0, 0, 0, height.toInt()) binding.recyclerView.updatePadding(bottom = height.toInt())
} }
}) })
binding.recyclerView.apply { binding.recyclerView.apply {
@ -226,11 +226,6 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
hideKeyboard(view) hideKeyboard(view)
} }
override fun onResume() {
super.onResume()
mainActivity.setBottomNavVisibility(false)
}
private fun hideKeyboard(view: View?) { private fun hideKeyboard(view: View?) {
if (view != null) { if (view != null) {
val imm: InputMethodManager = val imm: InputMethodManager =

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,12 +15,10 @@
package code.name.monkey.retromusic.helper package code.name.monkey.retromusic.helper
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Activity import android.app.Activity
import android.content.* import android.content.*
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.IBinder import android.os.IBinder
import android.provider.DocumentsContract import android.provider.DocumentsContract
@ -440,12 +438,14 @@ object MusicPlayerRemote : KoinComponent {
} else if (uri.authority == "media") { } else if (uri.authority == "media") {
songId = uri.lastPathSegment songId = uri.lastPathSegment
} }
if (songId != null) { songs = if (songId != null) {
songs = songRepository.songs(songId) songRepository.songs(songId)
} else {
songRepository.songsIgnoreBlacklist(uri)
} }
} }
} }
if (songs == null) { if (songs == null || songs.isEmpty()) {
var songFile: File? = null var songFile: File? = null
if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") { if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") {
songFile = File( songFile = File(
@ -462,7 +462,7 @@ object MusicPlayerRemote : KoinComponent {
songFile = File(uri.path!!) songFile = File(uri.path!!)
} }
if (songFile != null) { if (songFile != null) {
songs = songRepository.songsByFilePath(songFile.absolutePath) songs = songRepository.songsByFilePath(songFile.absolutePath, true)
} }
} }
if (songs != null && songs.isNotEmpty()) { if (songs != null && songs.isNotEmpty()) {
@ -474,7 +474,6 @@ object MusicPlayerRemote : KoinComponent {
} }
} }
@TargetApi(Build.VERSION_CODES.KITKAT)
private fun getSongIdFromMediaProvider(uri: Uri): String { private fun getSongIdFromMediaProvider(uri: Uri): String {
return DocumentsContract.getDocumentId(uri).split(":".toRegex()) return DocumentsContract.getDocumentId(uri).split(":".toRegex())
.dropLastWhile { it.isEmpty() }.toTypedArray()[1] .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 mDefaultLabel: String? = null
private var mLrcPadding = 0f private var mLrcPadding = 0f
private var mOnPlayClickListener: OnPlayClickListener? = null private var mOnPlayClickListener: OnPlayClickListener? = null
private var mOnFlingXListener: OnFlingXListener? = null
private var mAnimator: ValueAnimator? = null private var mAnimator: ValueAnimator? = null
private var mGestureDetector: GestureDetector? = null private var mGestureDetector: GestureDetector? = null
private var mScroller: Scroller? = null private var mScroller: Scroller? = null
@ -132,9 +131,6 @@ class CoverLrcView @JvmOverloads constructor(
velocityX: Float, velocityX: Float,
velocityY: Float velocityY: Float
): Boolean { ): Boolean {
if (mOnFlingXListener != null && abs(velocityX) > abs(velocityY)) {
return mOnFlingXListener!!.onFlingX(velocityX)
}
if (hasLrc()) { if (hasLrc()) {
mScroller!!.fling( mScroller!!.fling(
0, 0,
@ -167,7 +163,7 @@ class CoverLrcView @JvmOverloads constructor(
isShowTimeline = false isShowTimeline = false
removeCallbacks(hideTimelineRunnable) removeCallbacks(hideTimelineRunnable)
mCurrentLine = centerLine mCurrentLine = centerLine
invalidate() animateCurrentTextSize()
return true return true
} }
} else { } else {
@ -320,10 +316,6 @@ class CoverLrcView @JvmOverloads constructor(
mOnPlayClickListener = onPlayClickListener mOnPlayClickListener = onPlayClickListener
} }
fun setOnFlingXListener(onFlingXListener: OnFlingXListener) {
mOnFlingXListener = onFlingXListener
}
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */ /** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
fun setLabel(label: String?) { fun setLabel(label: String?) {
runOnUi { runOnUi {
@ -458,6 +450,7 @@ class CoverLrcView @JvmOverloads constructor(
mCurrentLine = line mCurrentLine = line
if (!isShowTimeline) { if (!isShowTimeline) {
smoothScrollTo(line) smoothScrollTo(line)
animateCurrentTextSize()
} else { } else {
invalidate() invalidate()
} }
@ -536,6 +529,18 @@ class CoverLrcView @JvmOverloads constructor(
canvas.restore() canvas.restore()
} }
fun animateCurrentTextSize() {
val currentTextSize = mCurrentTextSize
ValueAnimator.ofFloat(mNormalTextSize, currentTextSize).apply {
addUpdateListener {
mCurrentTextSize = it.animatedValue as Float
invalidate()
}
duration = 300L
start()
}
}
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean { override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_UP if (event.action == MotionEvent.ACTION_UP
@ -613,7 +618,7 @@ class CoverLrcView @JvmOverloads constructor(
private fun adjustCenter() { private fun adjustCenter() {
smoothScrollTo(centerLine, ADJUST_DURATION) smoothScrollTo(centerLine, ADJUST_DURATION)
} }
/** 滚动到某一行 */
/** 滚动到某一行 */ /** 滚动到某一行 */
private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) { private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) {
val offset = getOffset(line) val offset = getOffset(line)

View file

@ -37,6 +37,7 @@ data class CategoryInfo(
Artists(R.id.action_artist, R.string.artists, R.drawable.asld_artist), Artists(R.id.action_artist, R.string.artists, R.drawable.asld_artist),
Playlists(R.id.action_playlist, R.string.playlists, R.drawable.asld_playlist), Playlists(R.id.action_playlist, R.string.playlists, R.drawable.asld_playlist),
Genres(R.id.action_genre, R.string.genres, R.drawable.asld_guitar), 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 package code.name.monkey.retromusic.preferences
import android.annotation.SuppressLint
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
@ -22,8 +21,6 @@ import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN 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.appthemehelper.common.prefs.supportv7.ATEDialogPreference
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.PreferenceDialogNowPlayingScreenBinding
import code.name.monkey.retromusic.databinding.PreferenceNowPlayingScreenItemBinding
import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.AlbumCoverStyle import code.name.monkey.retromusic.fragments.AlbumCoverStyle
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 private var viewPagerPosition: Int = 0
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@SuppressLint("InflateParams") val view = val binding = PreferenceDialogNowPlayingScreenBinding.inflate(layoutInflater)
layoutInflater binding.nowPlayingScreenViewPager.apply {
.inflate(R.layout.preference_dialog_now_playing_screen, null) adapter = AlbumCoverStyleAdapter(requireContext())
val viewPager = view.findViewById<ViewPager>(R.id.now_playing_screen_view_pager) addOnPageChangeListener(this@AlbumCoverStylePreferenceDialog)
viewPager.adapter = AlbumCoverStyleAdapter(requireContext()) pageMargin = ViewUtil.convertDpToPixel(32f, resources).toInt()
viewPager.addOnPageChangeListener(this) currentItem = PreferenceUtil.albumCoverStyle.ordinal
viewPager.pageMargin = ViewUtil.convertDpToPixel(32f, resources).toInt() }
viewPager.currentItem = PreferenceUtil.albumCoverStyle.ordinal
return materialDialog(R.string.pref_title_album_cover_style) return materialDialog(R.string.pref_title_album_cover_style)
.setPositiveButton(R.string.set) { _, _ -> .setPositiveButton(R.string.set) { _, _ ->
@ -89,7 +87,7 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
PreferenceUtil.albumCoverStyle = coverStyle PreferenceUtil.albumCoverStyle = coverStyle
} }
} }
.setView(view) .setView(binding.root)
.create() .create()
.colorButtons() .colorButtons()
} }
@ -111,25 +109,17 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
val albumCoverStyle = values()[position] val albumCoverStyle = values()[position]
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
val layout = inflater.inflate( val binding = PreferenceNowPlayingScreenItemBinding.inflate(inflater, collection, true)
R.layout.preference_now_playing_screen_item,
collection,
false
) as ViewGroup
collection.addView(layout)
val image = layout.findViewById<ImageView>(R.id.image) Glide.with(context).load(albumCoverStyle.drawableResId).into(binding.image)
val title = layout.findViewById<TextView>(R.id.title) binding.title.setText(albumCoverStyle.titleRes)
val proText = layout.findViewById<TextView>(R.id.proText)
Glide.with(context).load(albumCoverStyle.drawableResId).into(image)
title.setText(albumCoverStyle.titleRes)
if (isAlbumCoverStyle(albumCoverStyle)) { if (isAlbumCoverStyle(albumCoverStyle)) {
proText.show() binding.proText.show()
proText.setText(R.string.pro) binding.proText.setText(R.string.pro)
} else { } else {
proText.hide() binding.proText.hide()
} }
return layout return binding.root
} }
override fun destroyItem( 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 package code.name.monkey.retromusic.preferences
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.util.AttributeSet import android.util.AttributeSet
import android.widget.TextView import android.widget.TextView
import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN import androidx.core.graphics.BlendModeCompat.SRC_IN
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreference import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreference
import code.name.monkey.retromusic.R 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.colorButtons
import code.name.monkey.retromusic.extensions.colorControlNormal import code.name.monkey.retromusic.extensions.colorControlNormal
import code.name.monkey.retromusic.extensions.materialDialog import code.name.monkey.retromusic.extensions.materialDialog
@ -50,29 +36,24 @@ class DurationPreference @JvmOverloads constructor(
class DurationPreferenceDialog : DialogFragment() { class DurationPreferenceDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val view = layoutInflater val binding = PreferenceDialogAudioFadeBinding.inflate(layoutInflater)
.inflate(R.layout.preference_dialog_audio_fade, null)
binding.slider.apply {
val slider = view.findViewById<Slider>(R.id.slider) addAccentColor()
val duration = view.findViewById<TextView>(R.id.duration) value = PreferenceUtil.audioFadeDuration.toFloat()
ColorStateList.valueOf(ThemeStore.accentColor(requireContext())).let { updateText(value.toInt(), binding.duration)
slider.trackTintList = it addOnChangeListener(Slider.OnChangeListener { _, value, fromUser ->
slider.thumbTintList = it if (fromUser) {
updateText(value.toInt(), binding.duration)
}
})
} }
slider.value = PreferenceUtil.audioFadeDuration.toFloat()
updateText(slider.value.toInt(), duration)
slider.addOnChangeListener(Slider.OnChangeListener { _, value, fromUser ->
if (fromUser) {
updateText(value.toInt(), duration)
}
})
return materialDialog(R.string.audio_fade_duration) return materialDialog(R.string.audio_fade_duration)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.save) { _, _ -> updateDuration(slider.value.toInt()) } .setPositiveButton(R.string.save) { _, _ -> updateDuration(binding.slider.value.toInt()) }
.setView(view) .setView(binding.root)
.create() .create()
.colorButtons() .colorButtons()
} }

View file

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

View file

@ -21,8 +21,6 @@ import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN 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.appthemehelper.common.prefs.supportv7.ATEDialogPreference
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.PreferenceNowPlayingScreenItemBinding
import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.NowPlayingScreen import code.name.monkey.retromusic.fragments.NowPlayingScreen
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 nowPlayingScreen = values()[position]
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
val layout = inflater.inflate( val binding = PreferenceNowPlayingScreenItemBinding.inflate(inflater, collection, true)
R.layout.preference_now_playing_screen_item, Glide.with(context).load(nowPlayingScreen.drawableResId).into(binding.image)
collection, binding.title.setText(nowPlayingScreen.titleRes)
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)
if (isNowPlayingThemes(nowPlayingScreen)) { if (isNowPlayingThemes(nowPlayingScreen)) {
proText.show() binding.proText.show()
proText.setText(R.string.pro) binding.proText.setText(R.string.pro)
}else{ } else {
proText.hide() binding.proText.hide()
} }
return layout return binding.root
} }
override fun destroyItem( override fun destroyItem(

View file

@ -69,6 +69,7 @@ interface Repository {
suspend fun recentAlbumsHome(): Home suspend fun recentAlbumsHome(): Home
suspend fun favoritePlaylistHome(): Home suspend fun favoritePlaylistHome(): Home
suspend fun suggestionsHome(): Home suspend fun suggestionsHome(): Home
suspend fun suggestions(): List<Song>
suspend fun genresHome(): Home suspend fun genresHome(): Home
suspend fun playlists(): Home suspend fun playlists(): Home
suspend fun homeSections(): List<Home> suspend fun homeSections(): List<Home>
@ -240,19 +241,15 @@ class RealRepository(
override suspend fun homeSections(): List<Home> { override suspend fun homeSections(): List<Home> {
val homeSections = mutableListOf<Home>() val homeSections = mutableListOf<Home>()
val sections: List<Home> = listOf( val sections: List<Home> = listOf(
suggestionsHome(),
topArtistsHome(), topArtistsHome(),
topAlbumsHome(), topAlbumsHome(),
recentArtistsHome(), recentArtistsHome(),
recentAlbumsHome(), recentAlbumsHome(),
favoritePlaylistHome() favoritePlaylistHome()
// genresHome()
) )
for (section in sections) { for (section in sections) {
if (section.arrayList.isNotEmpty()) { if (section.arrayList.isNotEmpty()) {
if (section.homeSection != SUGGESTIONS || PreferenceUtil.homeSuggestions) { homeSections.add(section)
homeSections.add(section)
}
} }
} }
return homeSections return homeSections
@ -374,6 +371,13 @@ class RealRepository(
return suggestions 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 { override suspend fun genresHome(): Home {
val genres = genreRepository.genres().shuffled() val genres = genreRepository.genres().shuffled()
return Home(genres, GENRES, R.string.genres) 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.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri
import android.os.Environment import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
@ -42,11 +43,13 @@ interface SongRepository {
fun songs(query: String): List<Song> 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(cursor: Cursor?): Song
fun song(songId: Long): Song fun song(songId: Long): Song
fun songsIgnoreBlacklist(uri: Uri): List<Song>
} }
class RealSongRepository(private val context: Context) : SongRepository { 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()))) 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( return songs(
makeSongCursor( makeSongCursor(
AudioColumns.DATA + "=?", 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( private fun getSongFromCursorImpl(
cursor: Cursor cursor: Cursor
): Song { ): Song {
@ -128,40 +154,41 @@ class RealSongRepository(private val context: Context) : SongRepository {
@JvmOverloads @JvmOverloads
fun makeSongCursor( fun makeSongCursor(
selection: String?, selection: String?,
selectionValues: Array<String>?, selectionValues: Array<String>?,
sortOrder: String = PreferenceUtil.songSortOrder sortOrder: String = PreferenceUtil.songSortOrder,
ignoreBlacklist: Boolean = false
): Cursor? { ): Cursor? {
var selectionFinal = selection var selectionFinal = selection
var selectionValuesFinal = selectionValues var selectionValuesFinal = selectionValues
selectionFinal = if (selection != null && selection.trim { it <= ' ' } != "") { if (!ignoreBlacklist) {
"$IS_MUSIC AND $selectionFinal" selectionFinal = if (selection != null && selection.trim { it <= ' ' } != "") {
} else { "$IS_MUSIC AND $selectionFinal"
IS_MUSIC } else {
} IS_MUSIC
// Whitelist
if (PreferenceUtil.isWhiteList) {
selectionFinal =
selectionFinal + " AND " + AudioColumns.DATA + " LIKE ?"
selectionValuesFinal = addSelectionValues(
selectionValuesFinal, arrayListOf(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).canonicalPath
)
)
} else {
// Blacklist
val paths = BlacklistStore.getInstance(context).paths
if (paths.isNotEmpty()) {
selectionFinal = generateBlacklistSelection(selectionFinal, paths.size)
selectionValuesFinal = addSelectionValues(selectionValuesFinal, paths)
} }
// Whitelist
if (PreferenceUtil.isWhiteList) {
selectionFinal =
selectionFinal + " AND " + AudioColumns.DATA + " LIKE ?"
selectionValuesFinal = addSelectionValues(
selectionValuesFinal, arrayListOf(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).canonicalPath
)
)
} else {
// Blacklist
val paths = BlacklistStore.getInstance(context).paths
if (paths.isNotEmpty()) {
selectionFinal = generateBlacklistSelection(selectionFinal, paths.size)
selectionValuesFinal = addSelectionValues(selectionValuesFinal, paths)
}
}
selectionFinal =
selectionFinal + " AND " + Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000)
} }
selectionFinal =
selectionFinal + " AND " + Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000)
val uri = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { val uri = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
Media.getContentUri(MediaStore.VOLUME_EXTERNAL) Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else { } else {

View file

@ -16,8 +16,10 @@ package code.name.monkey.retromusic.service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.PlaybackParams;
import android.media.audiofx.AudioEffect; import android.media.audiofx.AudioEffect;
import android.net.Uri; import android.net.Uri;
import android.os.PowerManager; import android.os.PowerManager;
@ -26,29 +28,37 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.R;
import code.name.monkey.retromusic.service.playback.Playback; import code.name.monkey.retromusic.service.playback.Playback;
import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.PreferenceUtil;
/** @author Andrew Neal, Karim Abou Zeid (kabouzeid) */ /**
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
*/
public class MultiPlayer public class MultiPlayer
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = MultiPlayer.class.getSimpleName(); public static final String TAG = MultiPlayer.class.getSimpleName();
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer(); private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
private MediaPlayer mNextMediaPlayer; private MediaPlayer mNextMediaPlayer;
private final Context context; private final Context context;
@Nullable @Nullable
private Playback.PlaybackCallbacks callbacks; private Playback.PlaybackCallbacks callbacks;
private boolean mIsInitialized = false; private boolean mIsInitialized = false;
/** Constructor of <code>MultiPlayer</code> */ /**
* Constructor of <code>MultiPlayer</code>
*/
MultiPlayer(final Context context) { MultiPlayer(final Context context) {
this.context = context; this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
PreferenceManager.getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(this);
} }
/** /**
@ -67,7 +77,7 @@ public class MultiPlayer
/** /**
* @param player The {@link MediaPlayer} to use * @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream you want to play * @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise * @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/ */
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) { private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
@ -82,6 +92,7 @@ public class MultiPlayer
} else { } else {
player.setDataSource(path); player.setDataSource(path);
} }
setPlaybackSpeedPitch(player);
player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.prepare(); player.prepare();
} catch (Exception e) { } catch (Exception e) {
@ -155,13 +166,17 @@ public class MultiPlayer
this.callbacks = callbacks; this.callbacks = callbacks;
} }
/** @return True if the player is ready to go, false otherwise */ /**
* @return True if the player is ready to go, false otherwise
*/
@Override @Override
public boolean isInitialized() { public boolean isInitialized() {
return mIsInitialized; return mIsInitialized;
} }
/** Starts or resumes playback. */ /**
* Starts or resumes playback.
*/
@Override @Override
public boolean start() { public boolean start() {
try { try {
@ -172,14 +187,18 @@ public class MultiPlayer
} }
} }
/** Resets the MediaPlayer to its uninitialized state. */ /**
* Resets the MediaPlayer to its uninitialized state.
*/
@Override @Override
public void stop() { public void stop() {
mCurrentMediaPlayer.reset(); mCurrentMediaPlayer.reset();
mIsInitialized = false; mIsInitialized = false;
} }
/** Releases resources associated with this MediaPlayer object. */ /**
* Releases resources associated with this MediaPlayer object.
*/
@Override @Override
public void release() { public void release() {
stop(); stop();
@ -187,9 +206,12 @@ public class MultiPlayer
if (mNextMediaPlayer != null) { if (mNextMediaPlayer != null) {
mNextMediaPlayer.release(); mNextMediaPlayer.release();
} }
PreferenceManager.getDefaultSharedPreferences(context).unregisterOnSharedPreferenceChangeListener(this);
} }
/** Pauses playback. Call start() to resume. */ /**
* Pauses playback. Call start() to resume.
*/
@Override @Override
public boolean pause() { public boolean pause() {
try { try {
@ -200,7 +222,9 @@ public class MultiPlayer
} }
} }
/** Checks whether the MultiPlayer is playing. */ /**
* Checks whether the MultiPlayer is playing.
*/
@Override @Override
public boolean isPlaying() { public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying(); return mIsInitialized && mCurrentMediaPlayer.isPlaying();
@ -291,7 +315,9 @@ public class MultiPlayer
return mCurrentMediaPlayer.getAudioSessionId(); return mCurrentMediaPlayer.getAudioSessionId();
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) { public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false; mIsInitialized = false;
@ -308,7 +334,9 @@ public class MultiPlayer
return false; return false;
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public void onCompletion(final MediaPlayer mp) { public void onCompletion(final MediaPlayer mp) {
if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) { if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) {
@ -323,6 +351,26 @@ public class MultiPlayer
} }
} }
@Override @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.CLASSIC_NOTIFICATION;
import static code.name.monkey.retromusic.ConstantsKt.COLORED_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.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.ConstantsKt.TOGGLE_HEADSET;
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator; 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.activities.LockScreenActivity;
import code.name.monkey.retromusic.appwidgets.AppWidgetBig; import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
import code.name.monkey.retromusic.appwidgets.AppWidgetCard; 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.AppWidgetClassic;
import code.name.monkey.retromusic.appwidgets.AppWidgetMD3; import code.name.monkey.retromusic.appwidgets.AppWidgetMD3;
import code.name.monkey.retromusic.appwidgets.AppWidgetSmall; 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 META_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".metachanged";
public static final String QUEUE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".queuechanged"; 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 PLAY_STATE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".playstatechanged";
public static final String FAVORITE_STATE_CHANGED = public static final String FAVORITE_STATE_CHANGED =
RETRO_MUSIC_PACKAGE_NAME + "favoritestatechanged"; RETRO_MUSIC_PACKAGE_NAME + "favoritestatechanged";
public static final String REPEAT_MODE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".repeatmodechanged"; 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 AppWidgetMD3 appWidgetMd3 = AppWidgetMD3.Companion.getInstance();
private final AppWidgetCircle appWidgetCircle = AppWidgetCircle.Companion.getInstance();
private final BroadcastReceiver widgetIntentReceiver = private final BroadcastReceiver widgetIntentReceiver =
new BroadcastReceiver() { new BroadcastReceiver() {
@Override @Override
@ -236,6 +241,10 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetMd3.performUpdate(MusicService.this, ids); appWidgetMd3.performUpdate(MusicService.this, ids);
break; 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) { public void onReceive(final Context context, final Intent intent) {
playingNotification.updateFavorite(getCurrentSong(), MusicService.this::startForegroundOrNotify); playingNotification.updateFavorite(getCurrentSong(), MusicService.this::startForegroundOrNotify);
startForegroundOrNotify(); startForegroundOrNotify();
appWidgetCircle.notifyChange(MusicService.this, FAVORITE_STATE_CHANGED);
} }
}; };
private final BroadcastReceiver lockScreenReceiver = private final BroadcastReceiver lockScreenReceiver =
@ -431,9 +441,7 @@ public class MusicService extends MediaBrowserServiceCompat
registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
setSessionToken(mediaSession.getSessionToken()); setSessionToken(mediaSession.getSessionToken());
if (VersionUtils.INSTANCE.hasMarshmallow()) { notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager = getSystemService(NotificationManager.class);
}
initNotification(); initNotification();
mediaStoreObserver = new MediaStoreObserver(this, playerHandler); mediaStoreObserver = new MediaStoreObserver(this, playerHandler);
@ -939,8 +947,11 @@ public class MusicService extends MediaBrowserServiceCompat
break; break;
case CLASSIC_NOTIFICATION: case CLASSIC_NOTIFICATION:
updateNotification(); updateNotification();
playingNotification.setPlaying(isPlaying());
playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify); playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify);
playingNotification.setPlaying(isPlaying(),this::startForegroundOrNotify);
break;
case PLAYBACK_SPEED:
updateMediaSessionPlaybackState();
break; break;
case TOGGLE_HEADSET: case TOGGLE_HEADSET:
registerHeadsetEvents(); registerHeadsetEvents();
@ -1336,7 +1347,7 @@ public class MusicService extends MediaBrowserServiceCompat
.setState( .setState(
isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED, isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
getSongProgressMillis(), getSongProgressMillis(),
1); PreferenceUtil.INSTANCE.getPlaybackSpeed());
setCustomAction(stateBuilder); setCustomAction(stateBuilder);
@ -1426,7 +1437,7 @@ public class MusicService extends MediaBrowserServiceCompat
savePositionInTrack(); savePositionInTrack();
} }
songPlayCountHelper.notifyPlayStateChanged(isPlaying); songPlayCountHelper.notifyPlayStateChanged(isPlaying);
playingNotification.setPlaying(isPlaying); playingNotification.setPlaying(isPlaying, this::startForegroundOrNotify);
startForegroundOrNotify(); startForegroundOrNotify();
break; break;
case FAVORITE_STATE_CHANGED: case FAVORITE_STATE_CHANGED:
@ -1617,6 +1628,7 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetCard.notifyChange(this, what); appWidgetCard.notifyChange(this, what);
appWidgetText.notifyChange(this, what); appWidgetText.notifyChange(this, what);
appWidgetMd3.notifyChange(this, what); appWidgetMd3.notifyChange(this, what);
appWidgetCircle.notifyChange(this, what);
} }
private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) { private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) {

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