Merge pull request #1178 from prathameshmm02/dev

Bug Fixes & Improvements
main
Daksh P. Jain 2021-12-05 15:11:41 +05:30 committed by GitHub
commit 350cba2042
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1275 additions and 749 deletions

View File

@ -15,8 +15,8 @@ android {
vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic"
versionCode 10544
versionName '5.4.1 ' + "_" + getDate()
versionCode 10545
versionName '5.4.2 ' + "_" + getDate()
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
}
@ -94,8 +94,8 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01'
implementation 'androidx.preference:preference-ktx:1.2.0-beta01'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.palette:palette-ktx:1.0.0'
@ -110,7 +110,7 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
def room_version = '2.4.0-beta02'
def room_version = '2.4.0-rc01'
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
@ -138,7 +138,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
def kotlin_coroutines_version = '1.5.2'
def kotlin_coroutines_version = '1.6.0-RC'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"

View File

@ -121,7 +121,7 @@
<activity android:name=".activities.PermissionActivity" />
<activity android:name=".activities.LockScreenActivity" />
<activity
android:name="code.name.monkey.retromusic.fragments.backup.RestoreActivity"
android:name=".fragments.backup.RestoreActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -256,13 +256,25 @@
android:name="android.appwidget.provider"
android:resource="@xml/app_widget_card_info" />
</receiver>
<receiver
android:name=".appwidgets.AppWidgetMD3"
android:exported="true"
android:label="@string/app_widget_md3_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_md3_info" />
</receiver>
<service
android:name=".service.MusicService"
android:enabled="true"
android:exported="true"
android:label="@string/app_name"
android:foregroundServiceType="mediaPlayback"
android:label="@string/app_name"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />

View File

@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.databinding.ActivityDriveModeBinding
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.glide.BlurTransformation
import code.name.monkey.retromusic.glide.GlideApp

View File

@ -24,7 +24,7 @@ import androidx.core.view.ViewCompat
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.databinding.ActivityLockScreenBinding
import code.name.monkey.retromusic.extensions.whichFragment
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenControlsFragment
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
@ -47,7 +47,7 @@ class LockScreenActivity : AbsMusicServiceActivity() {
binding = ActivityLockScreenBinding.inflate(layoutInflater)
setContentView(binding.root)
hideStatusBar()
setStatusbarColorAuto()
setStatusBarColorAuto()
setTaskDescriptionColorAuto()
val config = SlidrConfig.Builder().listener(object : SlidrListener {

View File

@ -26,9 +26,7 @@ import androidx.navigation.ui.setupWithNavController
import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.activities.base.AbsCastActivity
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
import code.name.monkey.retromusic.extensions.currentFragment
import code.name.monkey.retromusic.extensions.extra
import code.name.monkey.retromusic.extensions.findNavController
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.fragments.home.HomeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -136,8 +134,8 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
val expand = extra<Boolean>(EXPAND_PANEL).value ?: false
if (expand && PreferenceUtil.isExpandPanel) {
setBottomNavVisibility(false)
fromNotification = true
slidingPanel.bringToFront()
expandPanel()
intent.removeExtra(EXPAND_PANEL)
}

View File

@ -30,6 +30,8 @@ import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.databinding.ActivityPermissionBinding
import code.name.monkey.retromusic.extensions.accentBackgroundColor
import code.name.monkey.retromusic.extensions.setStatusBarColorAuto
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.util.RingtoneManager
@ -40,7 +42,7 @@ class PermissionActivity : AbsMusicServiceActivity() {
super.onCreate(savedInstanceState)
binding = ActivityPermissionBinding.inflate(layoutInflater)
setContentView(binding.root)
setStatusbarColorAuto()
setStatusBarColorAuto()
setTaskDescriptionColorAuto()
setupTitle()

View File

@ -28,6 +28,9 @@ import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.databinding.ActivityProVersionBinding
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
import code.name.monkey.retromusic.extensions.setLightStatusBar
import code.name.monkey.retromusic.extensions.setStatusBarColor
import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.PurchaseInfo
@ -41,8 +44,8 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
super.onCreate(savedInstanceState)
binding = ActivityProVersionBinding.inflate(layoutInflater)
setContentView(binding.root)
setStatusbarColor(Color.TRANSPARENT)
setLightStatusbar(false)
setStatusBarColor(Color.TRANSPARENT)
setLightStatusBar(false)
binding.toolbar.navigationIcon?.setTint(Color.WHITE)
binding.toolbar.setNavigationOnClickListener { onBackPressed() }

View File

@ -25,10 +25,7 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import code.name.monkey.retromusic.databinding.ActivitySettingsBinding
import code.name.monkey.retromusic.extensions.applyToolbar
import code.name.monkey.retromusic.extensions.extra
import code.name.monkey.retromusic.extensions.findNavController
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.extensions.*
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.color.ColorCallback
@ -38,7 +35,7 @@ class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListen
setDrawUnderStatusBar()
val mSavedInstanceState = extra<Bundle>(TAG).value ?: savedInstanceState
super.onCreate(mSavedInstanceState)
setLightStatusbarAuto(surfaceColor())
setLightStatusBarAuto(surfaceColor())
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
setupToolbar()

View File

@ -28,6 +28,10 @@ import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.databinding.ActivityShareInstagramBinding
import code.name.monkey.retromusic.extensions.applyToolbar
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
import code.name.monkey.retromusic.extensions.setLightStatusBar
import code.name.monkey.retromusic.extensions.setStatusBarColor
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
@ -60,7 +64,7 @@ class ShareInstagramStory : AbsBaseActivity() {
super.onCreate(savedInstanceState)
binding = ActivityShareInstagramBinding.inflate(layoutInflater)
setContentView(binding.root)
setStatusbarColor(Color.TRANSPARENT)
setStatusBarColor(Color.TRANSPARENT)
binding.toolbar.setBackgroundColor(Color.TRANSPARENT)
setSupportActionBar(binding.toolbar)
@ -104,7 +108,7 @@ class ShareInstagramStory : AbsBaseActivity() {
}
private fun setColors(colorLight: Boolean, color: Int) {
setLightStatusbar(colorLight)
setLightStatusBar(colorLight)
binding.toolbar.setTitleTextColor(
MaterialValueHelper.getPrimaryTextColor(
this@ShareInstagramStory,

View File

@ -37,6 +37,8 @@ import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.databinding.ActivityDonationBinding
import code.name.monkey.retromusic.extensions.setStatusBarColorAuto
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.extensions.textColorPrimary
import code.name.monkey.retromusic.extensions.textColorSecondary
import com.anjlab.android.iab.v3.BillingProcessor
@ -73,7 +75,7 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
binding = ActivityDonationBinding.inflate(layoutInflater)
setContentView(binding.root)
setStatusbarColorAuto()
setStatusBarColorAuto()
setTaskDescriptionColorAuto()
setupToolbar()

View File

@ -17,6 +17,8 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.databinding.ActivityWhatsNewBinding
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.setLightStatusBarAuto
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion
import code.name.monkey.retromusic.util.RetroUtil
import java.io.BufferedReader
@ -29,7 +31,7 @@ class WhatsNewActivity : AbsThemeActivity() {
super.onCreate(savedInstanceState)
val binding = ActivityWhatsNewBinding.inflate(layoutInflater)
setContentView(binding.root)
setLightStatusbarAuto(resolveColor(this, R.attr.colorSurface))
setLightStatusBarAuto(resolveColor(this, R.attr.colorSurface))
setTaskDescriptionColorAuto()
binding.toolbar.setNavigationOnClickListener { onBackPressed() }
ToolbarContentTintHelper.colorBackButton(binding.toolbar)

View File

@ -14,7 +14,6 @@
*/
package code.name.monkey.retromusic.activities.base
import android.animation.Animator
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
@ -25,6 +24,7 @@ import android.widget.FrameLayout
import androidx.core.animation.doOnEnd
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
@ -67,11 +67,10 @@ import org.koin.androidx.viewmodel.ext.android.viewModel
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
companion object {
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
var fromNotification: Boolean = false
}
var fromNotification = false
private var windowInsets: WindowInsetsCompat? = null
private var bottomNavAnimator: Animator? = null
protected val libraryViewModel by viewModel<LibraryViewModel>()
private lateinit var bottomSheetBehavior: RetroBottomSheetBehavior<FrameLayout>
private var playerFragment: AbsPlayerFragment? = null
@ -93,21 +92,19 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
when (newState) {
STATE_EXPANDED -> {
onPanelExpanded()
}
STATE_COLLAPSED -> {
onPanelCollapsed()
if (fromNotification) {
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty())
fromNotification = false
}
}
STATE_SETTLING, STATE_DRAGGING -> {
if (fromNotification) {
bottomNavigationView.isVisible = true
binding.bottomNavigationView.bringToFront()
fromNotification = false
}
}
else -> {
println("Do something")
println("Do a flip")
}
}
}
@ -175,7 +172,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
if (progress < 0) return
val alpha = 1 - progress
miniPlayerFragment?.view?.alpha = 1 - (progress / 0.2F)
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
miniPlayerFragment?.view?.isGone = alpha == 0f
binding.bottomNavigationView.translationY = progress * 500
binding.bottomNavigationView.alpha = alpha
binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
@ -184,9 +181,9 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
open fun onPanelCollapsed() {
setMiniPlayerAlphaProgress(0F)
// restore values
super.setLightStatusbarAuto(surfaceColor())
super.setLightNavigationAuto()
super.setTaskDescriptionColor(taskColor)
setLightStatusBarAuto(surfaceColor())
setLightNavigationAuto()
setTaskDescriptionColor(taskColor)
}
open fun onPanelExpanded() {
@ -217,6 +214,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
val bottomNavigationView get() = binding.bottomNavigationView
val slidingPanel get() = binding.slidingPanel
override fun onServiceConnected() {
super.onServiceConnected()
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
@ -251,32 +250,31 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
collapsePanel()
return true
}
return false
}
private fun onPaletteColorChanged() {
if (panelState == STATE_EXPANDED) {
super.setTaskDescriptionColor(paletteColor)
setTaskDescColor(paletteColor)
val isColorLight = ColorUtil.isColorLight(paletteColor)
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat)) {
super.setLightNavigationBar(true)
super.setLightStatusbar(isColorLight)
setLightNavigationBar(true)
setLightStatusBar(isColorLight)
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
super.setLightStatusbar(false)
super.setLightNavigationBar(true)
setLightStatusBar(false)
setLightNavigationBar(true)
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(isColorLight)
setLightNavigationBar(isColorLight)
setLightStatusBar(isColorLight)
} else if (nowPlayingScreen == Full) {
super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(false)
setLightNavigationBar(isColorLight)
setLightStatusBar(false)
} else if (nowPlayingScreen == Classic) {
super.setLightStatusbar(false)
setLightStatusBar(false)
} else if (nowPlayingScreen == Fit) {
super.setLightStatusbar(false)
setLightStatusBar(false)
} else {
super.setLightStatusbar(
setLightStatusBar(
ColorUtil.isColorLight(
ATHUtil.resolveColor(
this,
@ -284,15 +282,15 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
)
)
)
super.setLightNavigationBar(true)
setLightNavigationBar(true)
}
}
}
override fun setTaskDescriptionColor(color: Int) {
private fun setTaskDescColor(color: Int) {
taskColor = color
if (panelState == STATE_COLLAPSED) {
super.setTaskDescriptionColor(color)
setTaskDescriptionColor(color)
}
}
@ -319,53 +317,63 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
}
fun setBottomNavVisibility(visible: Boolean, animate: Boolean = false) {
binding.bottomNavigationView.isVisible = visible
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty(), animate)
val translationY =
if (visible) 0F else dip(R.dimen.bottom_nav_height).toFloat() + windowInsets.safeGetBottomInsets()
if (animate) {
binding.bottomNavigationView.translateYAnimate(translationY).doOnEnd {
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
binding.bottomNavigationView.bringToFront()
}
}
} else {
binding.bottomNavigationView.translationY =
translationY
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
binding.bottomNavigationView.bringToFront()
}
}
hideBottomSheet(
hide = MusicPlayerRemote.playingQueue.isEmpty(),
animate = animate,
isBottomNavVisible = visible
)
}
fun hideBottomSheet(hide: Boolean, animate: Boolean = false) {
fun hideBottomSheet(
hide: Boolean,
animate: Boolean = false,
isBottomNavVisible: Boolean = bottomNavigationView.isVisible
) {
val heightOfBar =
windowInsets.safeGetBottomInsets() +
if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height)
val heightOfBarWithTabs = heightOfBar + dip(R.dimen.bottom_nav_height)
val isVisible = binding.bottomNavigationView.isVisible
if (hide) {
bottomSheetBehavior.peekHeight = -windowInsets.safeGetBottomInsets()
bottomSheetBehavior.state = STATE_COLLAPSED
libraryViewModel.setFabMargin(if (isVisible) dip(R.dimen.bottom_nav_height) else 0)
libraryViewModel.setFabMargin(if (isBottomNavVisible) dip(R.dimen.bottom_nav_height) else 0)
ViewCompat.setElevation(binding.slidingPanel, 0f)
ViewCompat.setElevation(binding.bottomNavigationView, 10f)
} else {
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
ViewCompat.setElevation(binding.slidingPanel, 10f)
ViewCompat.setElevation(binding.bottomNavigationView, 10f)
if (isVisible) {
if (isBottomNavVisible) {
println("List")
if (animate) {
bottomNavAnimator?.end()
bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs)
bottomNavAnimator = binding.bottomNavigationView.translateYAnimate(0F)
} else {
bottomSheetBehavior.peekHeight = heightOfBarWithTabs
binding.bottomNavigationView.translationY = 0F
}
binding.bottomNavigationView.bringToFront()
libraryViewModel.setFabMargin(dip(R.dimen.mini_player_height_expanded))
} else {
println("Details")
if (animate) {
bottomSheetBehavior.peekHeightAnimate(heightOfBar)
bottomNavAnimator?.end()
bottomNavAnimator =
bottomNavigationView.translateYAnimate(dip(R.dimen.bottom_nav_height).toFloat())
bottomNavAnimator?.doOnEnd {
bottomSheetBehavior.peekHeightAnimate(heightOfBar).doOnEnd {
binding.slidingPanel.bringToFront()
}
} else {
bottomSheetBehavior.peekHeight = heightOfBar
binding.bottomNavigationView.translationY =
dip(R.dimen.bottom_nav_height).toFloat()
binding.slidingPanel.bringToFront()
}
libraryViewModel.setFabMargin(dip(R.dimen.mini_player_height))

View File

@ -16,28 +16,16 @@ package code.name.monkey.retromusic.activities.base
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.view.KeyEvent
import android.view.View
import android.view.WindowManager
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.core.os.ConfigurationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.LanguageContextWrapper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.theme.ThemeManager
import com.google.android.material.color.DynamicColors
import java.util.*
@ -53,9 +41,8 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
setImmersiveFullscreen()
registerSystemUiVisibility()
toggleScreenOn()
setDrawUnderNavigationBar()
setLightNavigationAuto()
setLightStatusbarAuto(surfaceColor())
setLightStatusBarAuto(surfaceColor())
}
private fun updateTheme() {
@ -71,14 +58,6 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
}
}
private fun toggleScreenOn() {
if (PreferenceUtil.isScreenOnEnabled) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
@ -90,95 +69,6 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
}
}
fun hideStatusBar() {
hideStatusBar(PreferenceUtil.isFullScreenMode)
}
private fun hideStatusBar(fullscreen: Boolean) {
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
if (statusBar != null) {
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
}
}
fun setDrawUnderStatusBar() {
RetroUtil.setAllowDrawUnderStatusBar(window)
}
private fun setDrawUnderNavigationBar() {
RetroUtil.setAllowDrawUnderNavigationBar(window)
}
/**
* This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On
* Lollipop if no such view is found it will set the statusbar color using the native method.
*
* @param color the new statusbar color (will be shifted down on Lollipop and above)
*/
fun setStatusbarColor(color: Int) {
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
if (statusBar != null) {
when {
VersionUtils.hasMarshmallow() -> statusBar.setBackgroundColor(color)
else -> statusBar.setBackgroundColor(
ColorUtil.darkenColor(
color
)
)
}
} else {
when {
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
else -> window.statusBarColor = ColorUtil.darkenColor(color)
}
}
setLightStatusbarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
}
fun setStatusbarColorAuto() {
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
setStatusbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
setLightStatusbarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
}
open fun setTaskDescriptionColor(@ColorInt color: Int) {
ATH.setTaskDescriptionColor(this, color)
}
fun setTaskDescriptionColorAuto() {
setTaskDescriptionColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
}
open fun setNavigationbarColor(color: Int) {
if (ThemeStore.coloredNavigationBar(this)) {
ATH.setNavigationbarColor(this, color)
} else {
ATH.setNavigationbarColor(this, Color.BLACK)
}
}
fun setNavigationbarColorAuto() {
setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
}
fun setLightNavigationAuto() {
ATH.setLightNavigationbarAuto(this, surfaceColor())
}
open fun setLightStatusbar(enabled: Boolean) {
ATH.setLightStatusbar(this, enabled)
}
fun setLightStatusbarAuto(bgColor: Int) {
setLightStatusbar(ColorUtil.isColorLight(bgColor))
}
open fun setLightNavigationBar(enabled: Boolean) {
if (!ATHUtil.isWindowBackgroundDark(this) and ThemeStore.coloredNavigationBar(this)) {
ATH.setLightNavigationbar(this, enabled)
}
}
private fun registerSystemUiVisibility() {
val decorView = window.decorView
decorView.setOnSystemUiVisibilityChangeListener { visibility ->
@ -193,23 +83,6 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
decorView.setOnSystemUiVisibilityChangeListener(null)
}
private fun setImmersiveFullscreen() {
if (PreferenceUtil.isFullScreenMode) {
WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
hide(WindowInsetsCompat.Type.systemBars())
}
}
}
private fun exitFullscreen() {
WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
show(WindowInsetsCompat.Type.systemBars())
}
}
override fun run() {
setImmersiveFullscreen()
}

View File

@ -41,6 +41,7 @@ import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubLogin
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubTarget
import code.name.monkey.retromusic.databinding.ActivityBugReportBinding
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.misc.DialogAsyncTask
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.FloatingActionButton

View File

@ -42,6 +42,7 @@ import code.name.monkey.retromusic.R.drawable
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.model.ArtworkInfo
import code.name.monkey.retromusic.model.AudioTagInfo
import code.name.monkey.retromusic.repository.Repository

View File

@ -32,6 +32,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
import code.name.monkey.retromusic.extensions.appHandleColor
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
import code.name.monkey.retromusic.extensions.setTint
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper

View File

@ -163,7 +163,7 @@ open class AlbumAdapter(
when (PreferenceUtil.albumSortOrder) {
SortOrder.AlbumSortOrder.ALBUM_A_Z, SortOrder.AlbumSortOrder.ALBUM_Z_A -> sectionName =
dataSet[position].title
SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].artistName
SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].albumArtist
SortOrder.AlbumSortOrder.ALBUM_YEAR -> return MusicUtil.getYearString(
dataSet[position].year
)

View File

@ -0,0 +1,273 @@
/*
* 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.text.TextUtils
import android.view.View
import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
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.*
import code.name.monkey.retromusic.util.DensityUtil
import code.name.monkey.retromusic.util.ImageUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
class AppWidgetMD3 : 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_md3)
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
appWidgetView.setImageViewBitmap(
R.id.button_next, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_skip_next,
secondaryColor
), 1f
)
)
appWidgetView.setImageViewBitmap(
R.id.button_prev, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_skip_previous,
secondaryColor
), 1f
)
)
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_play_arrow_white_32dp,
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_md3)
val isPlaying = service.isPlaying
val song = service.currentSong
// Set the titles and artwork
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else {
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
appWidgetView.setTextViewText(R.id.title, song.title)
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
}
// Set correct drawable for pause state
val playPauseRes =
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow_white_32dp
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
playPauseRes,
MaterialValueHelper.getSecondaryTextColor(service, true)
), 1f
)
)
// Set prev/next button drawables
appWidgetView.setImageViewBitmap(
R.id.button_next, createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_skip_next,
MaterialValueHelper.getSecondaryTextColor(service, true)
), 1f
)
)
appWidgetView.setImageViewBitmap(
R.id.button_prev, createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_skip_previous,
MaterialValueHelper.getSecondaryTextColor(service, true)
), 1f
)
)
// Link actions buttons to intents
linkButtons(service, appWidgetView)
if (imageSize == 0) {
imageSize =
service.resources.getDimensionPixelSize(R.dimen.app_widget_card_image_size)
}
if (cardRadius == 0f) {
cardRadius =
DensityUtil.dip2px(service, 8F).toFloat()
}
// 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))
.centerCrop()
.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 prev/next button drawables
appWidgetView.setImageViewBitmap(
R.id.button_next, ImageUtil.createBitmap(
ImageUtil.getTintedVectorDrawable(
service, R.drawable.ic_skip_next, color
)
)
)
appWidgetView.setImageViewBitmap(
R.id.button_prev, ImageUtil.createBitmap(
ImageUtil.getTintedVectorDrawable(
service, R.drawable.ic_skip_previous, color
)
)
)
val image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = createRoundedBitmap(
image, imageSize, imageSize, cardRadius, cardRadius, cardRadius, cardRadius
)
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
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)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
// Previous track
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
// Next track
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
}
companion object {
const val NAME = "app_widget_md3"
private var mInstance: AppWidgetMD3? = null
private var imageSize = 0
private var cardRadius = 0F
val instance: AppWidgetMD3
@Synchronized get() {
if (mInstance == null) {
mInstance = AppWidgetMD3()
}
return mInstance!!
}
}
}

View File

@ -0,0 +1,139 @@
package code.name.monkey.retromusic.extensions
import android.app.ActivityManager
import android.graphics.Color
import android.os.Build
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.FragmentActivity
import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
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.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
fun AppCompatActivity.toggleScreenOn() {
if (PreferenceUtil.isScreenOnEnabled) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
fun AppCompatActivity.setImmersiveFullscreen() {
if (PreferenceUtil.isFullScreenMode) {
WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
hide(WindowInsetsCompat.Type.systemBars())
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
}
fun AppCompatActivity.exitFullscreen() {
WindowInsetsControllerCompat(window, window.decorView).apply {
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
show(WindowInsetsCompat.Type.systemBars())
}
}
fun AppCompatActivity.hideStatusBar() {
hideStatusBar(PreferenceUtil.isFullScreenMode)
}
private fun AppCompatActivity.hideStatusBar(fullscreen: Boolean) {
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
if (statusBar != null) {
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
}
}
fun AppCompatActivity.setDrawUnderStatusBar() {
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.TRANSPARENT
}
fun FragmentActivity.setTaskDescriptionColor(color: Int) {
var colorFinal = color
// Task description requires fully opaque color
colorFinal = ColorUtil.stripAlpha(colorFinal)
// Sets color of entry in the system recents page
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setTaskDescription(
ActivityManager.TaskDescription(
title as String?,
-1,
colorFinal
)
)
} else {
setTaskDescription(ActivityManager.TaskDescription(title as String?))
}
}
fun AppCompatActivity.setTaskDescriptionColorAuto() {
setTaskDescriptionColor(surfaceColor())
}
fun AppCompatActivity.setLightNavigationAuto() {
ATH.setLightNavigationBarAuto(this, surfaceColor())
}
fun AppCompatActivity.setLightStatusBar(enabled: Boolean) {
ATH.setLightStatusBar(this, enabled)
}
fun AppCompatActivity.setLightStatusBarAuto(bgColor: Int) {
setLightStatusBar(ColorUtil.isColorLight(bgColor))
}
fun AppCompatActivity.setLightNavigationBar(enabled: Boolean) {
if (!ATHUtil.isWindowBackgroundDark(this) and ThemeStore.coloredNavigationBar(this)) {
ATH.setLightNavigationbar(this, enabled)
}
}
/**
* This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On
* Lollipop if no such view is found it will set the statusbar color using the native method.
*
* @param color the new statusbar color (will be shifted down on Lollipop and above)
*/
fun AppCompatActivity.setStatusBarColor(color: Int) {
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
if (statusBar != null) {
when {
VersionUtils.hasMarshmallow() -> statusBar.setBackgroundColor(color)
else -> statusBar.setBackgroundColor(
ColorUtil.darkenColor(
color
)
)
}
} else {
when {
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
else -> window.statusBarColor = ColorUtil.darkenColor(color)
}
}
setLightStatusBarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
}
fun AppCompatActivity.setStatusBarColorAuto() {
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
setStatusBarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
setLightStatusBarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
}

View File

@ -0,0 +1,39 @@
package code.name.monkey.retromusic.extensions
import android.net.Uri
import android.webkit.MimeTypeMap
import androidx.fragment.app.Fragment
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.RetroUtil
import org.jaudiotagger.audio.AudioFileIO
import java.io.File
import java.net.URLEncoder
fun getSongInfo(song: Song): String {
val file = File(song.data)
if (file.exists()) {
return try {
val audioHeader = AudioFileIO.read(File(song.data)).audioHeader
val string: StringBuilder = StringBuilder()
val uriFile = Uri.fromFile(file)
string.append(getMimeType(uriFile.toString())).append("")
string.append(audioHeader.bitRate).append(" kb/s").append("")
string.append(RetroUtil.frequencyCount(audioHeader.sampleRate.toInt()))
.append(" kHz")
string.toString()
} catch (er: Exception) {
" - "
}
}
return "-"
}
private fun getMimeType(url: String): String {
var type: String? = MimeTypeMap.getFileExtensionFromUrl(
URLEncoder.encode(url, "utf-8")
).uppercase()
if (type == null) {
type = url.substring(url.lastIndexOf(".") + 1)
}
return type
}

View File

@ -1,7 +1,9 @@
package code.name.monkey.retromusic.extensions
import androidx.core.view.WindowInsetsCompat
import code.name.monkey.retromusic.util.RetroUtil
fun WindowInsetsCompat?.safeGetBottomInsets(): Int {
return this?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: 0
// Get Navbar heights if insets are null
return (this?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: RetroUtil.getNavigationBarHeight())
}

View File

@ -37,6 +37,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.shape.ShapeAppearanceModel
@Suppress("UNCHECKED_CAST")
fun <T : View> ViewGroup.inflate(@LayoutRes layout: Int): T {
return LayoutInflater.from(context).inflate(layout, this, false) as T
@ -67,21 +68,22 @@ fun View.translateYAnimate(value: Float): Animator {
.apply {
duration = 300
doOnStart {
if (value == 0f) {
show()
}
show()
bringToFront()
}
doOnEnd {
if (value != 0f) {
hide()
} else {
show()
}
}
start()
}
}
fun BottomSheetBehavior<*>.peekHeightAnimate(value: Int) {
ObjectAnimator.ofInt(this, "peekHeight", value)
fun BottomSheetBehavior<*>.peekHeightAnimate(value: Int): Animator {
return ObjectAnimator.ofInt(this, "peekHeight", value)
.apply {
duration = 300
start()
@ -136,6 +138,7 @@ fun ShapeableImageView.setCircleShape(boolean: Boolean) {
* This will draw our view above the navigation bar instead of behind it by adding margins.
*/
fun View.drawAboveSystemBars(onlyPortrait: Boolean = true) {
if (PreferenceUtil.isFullScreenMode) return
if (onlyPortrait && RetroUtil.isLandscape()) return
// Create a snapshot of the view's margin state
val initialMargin = recordInitialMarginForView(this)
@ -157,6 +160,7 @@ fun View.drawAboveSystemBars(onlyPortrait: Boolean = true) {
* This will draw our view above the navigation bar instead of behind it by adding padding.
*/
fun View.drawAboveSystemBarsWithPadding(consume: Boolean = false) {
if (PreferenceUtil.isFullScreenMode) return
val initialPadding = recordInitialPaddingForView(this)
ViewCompat.setOnApplyWindowInsetsListener(

View File

@ -29,6 +29,7 @@ import code.name.monkey.retromusic.util.DensityUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
class LibraryViewModel(
@ -278,14 +279,16 @@ class LibraryViewModel(
}
emit(songs)
// Cleaning up deleted or moved songs
songs.forEach { song ->
if (!File(song.data).exists() || song.id == -1L) {
repository.deleteSongInPlayCount(song.toPlayCount())
withContext(IO) {
songs.forEach { song ->
if (!File(song.data).exists() || song.id == -1L) {
repository.deleteSongInPlayCount(song.toPlayCount())
}
}
emit(repository.playCountSongs().map {
it.toSong()
})
}
emit(repository.playCountSongs().map {
it.toSong()
})
}
fun artists(type: Int): LiveData<List<Artist>> = liveData {

View File

@ -196,7 +196,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
0,
R.id.action_album_sort_order_artist,
2,
R.string.sort_order_artist
R.string.sort_order_album_artist
).isChecked =
currentSortOrder.equals(AlbumSortOrder.ALBUM_ARTIST)
sortOrderMenu.add(

View File

@ -22,6 +22,8 @@ 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.activities.MainActivity
import code.name.monkey.retromusic.extensions.setLightStatusBarAuto
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.fragments.LibraryViewModel
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
@ -42,7 +44,7 @@ abstract class AbsMainActivityFragment(@LayoutRes layout: Int) : AbsMusicService
if (statusBar != null) {
if (VersionUtils.hasMarshmallow()) {
statusBar.setBackgroundColor(color)
mainActivity.setLightStatusbarAuto(color)
mainActivity.setLightStatusBarAuto(color)
} else {
statusBar.setBackgroundColor(color)
}

View File

@ -15,21 +15,14 @@
package code.name.monkey.retromusic.fragments.base
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.webkit.MimeTypeMap
import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment
import androidx.navigation.navOptions
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.RetroUtil
import org.jaudiotagger.audio.AudioFileIO
import java.io.File
import java.net.URLEncoder
/**
* Created by hemanths on 18/08/17.
@ -103,33 +96,4 @@ open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout),
override fun onMediaStoreChanged() {
}
fun getSongInfo(song: Song): String {
val file = File(song.data)
if (file.exists()) {
return try {
val audioHeader = AudioFileIO.read(File(song.data)).audioHeader
val string: StringBuilder = StringBuilder()
val uriFile = Uri.fromFile(file)
string.append(getMimeType(uriFile.toString())).append("")
string.append(audioHeader.bitRate).append(" kb/s").append("")
string.append(RetroUtil.frequencyCount(audioHeader.sampleRate.toInt()))
.append(" kHz")
string.toString()
} catch (er: Exception) {
" - "
}
}
return "-"
}
private fun getMimeType(url: String): String {
var type: String? = MimeTypeMap.getFileExtensionFromUrl(
URLEncoder.encode(url, "utf-8")
).uppercase()
if (type == null) {
type = url.substring(url.lastIndexOf(".") + 1)
}
return type
}
}

View File

@ -97,6 +97,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
enterTransition = Fade()
exitTransition = Fade()
lyricsSectionsAdapter = LyricsSectionsAdapter(requireActivity())

View File

@ -29,10 +29,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentAdaptivePlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.ripAlpha
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper

View File

@ -29,6 +29,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentBlurPlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment

View File

@ -27,6 +27,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentCardPlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.ripAlpha
import code.name.monkey.retromusic.extensions.show

View File

@ -27,6 +27,7 @@ import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentCardBlurPlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment

View File

@ -36,6 +36,7 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
import code.name.monkey.retromusic.databinding.FragmentClassicPlayerBinding
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment

View File

@ -32,6 +32,7 @@ import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentColorPlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment

View File

@ -29,6 +29,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFitPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.ripAlpha
import code.name.monkey.retromusic.extensions.show

View File

@ -28,10 +28,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFlatPlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.ripAlpha
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist

View File

@ -36,6 +36,7 @@ import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.LibraryViewModel

View File

@ -28,10 +28,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentPlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.ripAlpha
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist

View File

@ -22,6 +22,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentPeakPlayerBinding
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment

View File

@ -30,6 +30,7 @@ import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentPlainControlsFragmentBinding
import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment

View File

@ -25,6 +25,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentSimpleControlsFragmentBinding
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment

View File

@ -28,6 +28,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentTinyPlayerBinding
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment

View File

@ -18,9 +18,10 @@ import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter
import code.name.monkey.retromusic.databinding.FragmentPlaylistDetailBinding
import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.toSongs
import code.name.monkey.retromusic.extensions.dipToPix
import code.name.monkey.retromusic.extensions.dip
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
import code.name.monkey.retromusic.interfaces.ICabCallback
import code.name.monkey.retromusic.interfaces.ICabHolder
@ -123,8 +124,12 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
}
private fun checkForPadding() {
val height = dipToPix(52f)
binding.recyclerView.setPadding(0, 0, 0, height.toInt())
val itemCount: Int = playlistSongAdapter.itemCount
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
binding.recyclerView.updatePadding(bottom = dip(R.dimen.mini_player_height))
} else {
binding.recyclerView.updatePadding(bottom = 0)
}
}
private fun checkIsEmpty() {

View File

@ -19,15 +19,16 @@ import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.view.ViewCompat
import androidx.core.view.updatePadding
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat
import code.name.monkey.retromusic.activities.OnThemeChangedListener
import code.name.monkey.retromusic.extensions.safeGetBottomInsets
import code.name.monkey.retromusic.preferences.*
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.RetroUtil
/**
* @author Hemanth S (h4h13).
@ -66,16 +67,19 @@ abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setDivider(ColorDrawable(Color.TRANSPARENT))
// This is a workaround as CollapsingToolbarLayout consumes insets and
// insets are not passed to child views
// CollapsingToolbarLayout consumes insets and insets are not passed to child views
// So we get insets from root view
// https://github.com/material-components/material-components-android/issues/1310
if (!RetroUtil.isLandscape()) {
listView.updatePadding(bottom = RetroUtil.getNavigationBarHeight())
ViewCompat.setOnApplyWindowInsetsListener(
view
) { _, insets ->
listView.updatePadding(bottom = insets.safeGetBottomInsets())
insets
}
invalidateSettings()
}
override fun onDisplayPreferenceDialog(preference: Preference?) {
override fun onDisplayPreferenceDialog(preference: Preference) {
when (preference) {
is LibraryPreference -> {
val fragment = LibraryPreferenceDialog.newInstance()

View File

@ -33,10 +33,10 @@ import java.io.File
@GlideExtension
object RetroGlideExtension {
private const val DEFAULT_ERROR_ARTIST_IMAGE =
private const val DEFAULT_ARTIST_IMAGE =
R.drawable.default_artist_art
private const val DEFAULT_ERROR_SONG_IMAGE: Int = R.drawable.default_audio_art
private const val DEFAULT_ERROR_ALBUM_IMAGE = R.drawable.default_album_art
private const val DEFAULT_SONG_IMAGE: Int = R.drawable.default_audio_art
private const val DEFAULT_ALBUM_IMAGE = R.drawable.default_album_art
private const val DEFAULT_ERROR_IMAGE_BANNER = R.drawable.material_design_default
private val DEFAULT_DISK_CACHE_STRATEGY_ARTIST = DiskCacheStrategy.RESOURCE
@ -99,7 +99,8 @@ object RetroGlideExtension {
return baseRequestOptions
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY_ARTIST)
.priority(Priority.LOW)
.error(DEFAULT_ERROR_ARTIST_IMAGE)
.error(DEFAULT_ARTIST_IMAGE)
.placeholder(DEFAULT_ARTIST_IMAGE)
.signature(createSignature(artist))
}
@ -110,7 +111,8 @@ object RetroGlideExtension {
song: Song
): BaseRequestOptions<*> {
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
.error(DEFAULT_ERROR_SONG_IMAGE)
.error(DEFAULT_SONG_IMAGE)
.placeholder(DEFAULT_SONG_IMAGE)
.signature(createSignature(song))
}
@ -121,7 +123,8 @@ object RetroGlideExtension {
song: Song
): BaseRequestOptions<*> {
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
.error(DEFAULT_ERROR_ALBUM_IMAGE)
.error(DEFAULT_ALBUM_IMAGE)
.placeholder(DEFAULT_ALBUM_IMAGE)
.signature(createSignature(song))
}
@ -154,7 +157,7 @@ object RetroGlideExtension {
baseRequestOptions: BaseRequestOptions<*>
): BaseRequestOptions<*> {
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
.error(DEFAULT_ERROR_ALBUM_IMAGE)
.error(DEFAULT_ALBUM_IMAGE)
}
private fun createSignature(song: Song): Key {

View File

@ -33,8 +33,7 @@ abstract class RetroMusicColoredTarget(view: ImageView) : BitmapPaletteTarget(vi
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
val colors = MediaNotificationProcessor(App.getContext(), errorDrawable)
onColorReady(colors)
onColorReady(MediaNotificationProcessor.errorColor(App.getContext()))
}
override fun onResourceReady(

View File

@ -54,11 +54,11 @@ class SortOrder {
const val ALBUM_Z_A = "$ALBUM_A_Z DESC"
/* Album sort order songs */
const val ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS + " DESC"
const val ALBUM_NUMBER_OF_SONGS =
MediaStore.Audio.AlbumColumns.NUMBER_OF_SONGS + " DESC"
/* Album sort order artist */
const val ALBUM_ARTIST = (MediaStore.Audio.Artists.DEFAULT_SORT_ORDER +
", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER)
/* Album Artist sort order artist */
const val ALBUM_ARTIST = "case when lower(album_artist) is null then 1 else 0 end, lower(album_artist)"
/* Album sort order year */
const val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC"

View File

@ -25,6 +25,7 @@ import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothDevice;
@ -34,6 +35,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Point;
@ -81,6 +83,7 @@ import code.name.monkey.retromusic.activities.LockScreenActivity;
import code.name.monkey.retromusic.appwidgets.AppWidgetBig;
import code.name.monkey.retromusic.appwidgets.AppWidgetCard;
import code.name.monkey.retromusic.appwidgets.AppWidgetClassic;
import code.name.monkey.retromusic.appwidgets.AppWidgetMD3;
import code.name.monkey.retromusic.appwidgets.AppWidgetSmall;
import code.name.monkey.retromusic.appwidgets.AppWidgetText;
import code.name.monkey.retromusic.auto.AutoMediaIDHelper;
@ -105,6 +108,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil;
import code.name.monkey.retromusic.util.RetroUtil;
import code.name.monkey.retromusic.volume.AudioVolumeObserver;
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener;
import kotlin.Unit;
/**
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
@ -198,6 +202,8 @@ public class MusicService extends MediaBrowserServiceCompat
private final AppWidgetText appWidgetText = AppWidgetText.Companion.getInstance();
private final AppWidgetMD3 appWidgetMd3 = AppWidgetMD3.Companion.getInstance();
private final BroadcastReceiver widgetIntentReceiver =
new BroadcastReceiver() {
@Override
@ -226,6 +232,10 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetText.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetMD3.NAME: {
appWidgetMd3.performUpdate(MusicService.this, ids);
break;
}
}
}
}
@ -273,7 +283,8 @@ public class MusicService extends MediaBrowserServiceCompat
new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
updateNotification();
playingNotification.updateFavorite(getCurrentSong(), MusicService.this::startForegroundOrNotify);
startForegroundOrNotify();
}
};
private final BroadcastReceiver lockScreenReceiver =
@ -356,6 +367,8 @@ public class MusicService extends MediaBrowserServiceCompat
private ThrottledSeekHandler throttledSeekHandler;
private Handler uiThreadHandler;
private PowerManager.WakeLock wakeLock;
private NotificationManager notificationManager;
private boolean isForeground = false;
private static Bitmap copy(Bitmap bitmap) {
Bitmap.Config config = bitmap.getConfig();
@ -417,6 +430,10 @@ public class MusicService extends MediaBrowserServiceCompat
registerReceiver(updateFavoriteReceiver, new IntentFilter(FAVORITE_STATE_CHANGED));
registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
setSessionToken(mediaSession.getSessionToken());
if (VersionUtils.INSTANCE.hasMarshmallow()) {
notificationManager = getSystemService(NotificationManager.class);
}
initNotification();
mediaStoreObserver = new MediaStoreObserver(this, playerHandler);
@ -467,7 +484,6 @@ public class MusicService extends MediaBrowserServiceCompat
mPackageValidator = new PackageValidator(this, R.xml.allowed_media_browser_callers);
mMusicProvider.setMusicService(this);
setSessionToken(mediaSession.getSessionToken());
}
@Override
@ -765,11 +781,10 @@ public class MusicService extends MediaBrowserServiceCompat
public void initNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& !PreferenceUtil.INSTANCE.isClassicNotification()) {
playingNotification = new PlayingNotificationImpl();
playingNotification = PlayingNotificationImpl.Companion.from(this, notificationManager, mediaSession);
} else {
playingNotification = new PlayingNotificationOreo();
playingNotification = PlayingNotificationOreo.Companion.from(this, notificationManager);
}
playingNotification.init(this);
}
public boolean isLastTrack() {
@ -837,7 +852,8 @@ public class MusicService extends MediaBrowserServiceCompat
// Request from an untrusted package: return an empty browser root
return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null);
} else {
/** By default return the browsable root. Treat the EXTRA_RECENT flag as a special case
/**
* By default return the browsable root. Treat the EXTRA_RECENT flag as a special case
* and return the recent root instead.
*/
boolean isRecentRequest = false;
@ -882,7 +898,7 @@ public class MusicService extends MediaBrowserServiceCompat
/* Switch to MultiPlayer if Crossfade duration is 0 and
Playback is not an instance of MultiPlayer */
if (playback != null)
playback.setCrossFadeDuration(PreferenceUtil.INSTANCE.getCrossFadeDuration());
playback.setCrossFadeDuration(PreferenceUtil.INSTANCE.getCrossFadeDuration());
if (!(playback instanceof MultiPlayer) && PreferenceUtil.INSTANCE.getCrossFadeDuration() == 0) {
if (playback != null) {
playback.release();
@ -1042,6 +1058,7 @@ public class MusicService extends MediaBrowserServiceCompat
}
public void pause() {
Log.i(TAG, "Paused");
pausedByTransientLossOfFocus = false;
if (playback != null && playback.isPlaying()) {
startFadeAnimator(playback, false, () -> {
@ -1163,7 +1180,8 @@ public class MusicService extends MediaBrowserServiceCompat
public void quit() {
pause();
playingNotification.stop();
stopForeground(true);
notificationManager.cancel(PlayingNotification.NOTIFICATION_ID);
closeAudioEffectSession();
getAudioManager().abandonAudioFocus(audioFocusListener);
@ -1326,7 +1344,8 @@ public class MusicService extends MediaBrowserServiceCompat
public void updateNotification() {
if (playingNotification != null && getCurrentSong().getId() != -1) {
playingNotification.update();
stopForegroundAndNotification();
initNotification();
}
}
@ -1400,17 +1419,19 @@ public class MusicService extends MediaBrowserServiceCompat
private void handleChangeInternal(@NonNull final String what) {
switch (what) {
case PLAY_STATE_CHANGED:
updateNotification();
updateMediaSessionPlaybackState();
final boolean isPlaying = isPlaying();
if (!isPlaying && getSongProgressMillis() > 0) {
savePositionInTrack();
}
songPlayCountHelper.notifyPlayStateChanged(isPlaying);
playingNotification.setPlaying(isPlaying);
startForegroundOrNotify();
break;
case FAVORITE_STATE_CHANGED:
playingNotification.updateFavorite(getCurrentSong(), this::startForegroundOrNotify);
case META_CHANGED:
updateNotification();
playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify);
updateMediaSessionMetaData();
updateMediaSessionPlaybackState();
savePosition();
@ -1428,12 +1449,41 @@ public class MusicService extends MediaBrowserServiceCompat
if (playingQueue.size() > 0) {
prepareNext();
} else {
playingNotification.stop();
stopForegroundAndNotification();
}
break;
}
}
private Unit startForegroundOrNotify() {
if (!isForeground) {
// Specify that this is a media service, if supported.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
PlayingNotification.NOTIFICATION_ID, playingNotification.build(),
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
);
} else {
startForeground(PlayingNotification.NOTIFICATION_ID, playingNotification.build());
}
isForeground = true;
} else {
// If we are already in foreground just update the notification
notificationManager.notify(
PlayingNotification.NOTIFICATION_ID, playingNotification.build()
);
}
return Unit.INSTANCE;
}
private void stopForegroundAndNotification() {
stopForeground(true);
notificationManager.cancel(PlayingNotification.NOTIFICATION_ID);
isForeground = false;
}
private boolean openCurrent() {
synchronized (this) {
try {
@ -1551,6 +1601,7 @@ public class MusicService extends MediaBrowserServiceCompat
appWidgetSmall.notifyChange(this, what);
appWidgetCard.notifyChange(this, what);
appWidgetText.notifyChange(this, what);
appWidgetMd3.notifyChange(this, what);
}
private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) {
@ -1592,8 +1643,8 @@ public class MusicService extends MediaBrowserServiceCompat
mediaButtonIntent.setComponent(mediaButtonReceiverComponentName);
PendingIntent mediaButtonReceiverPendingIntent;
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent,
VersionUtils.INSTANCE.hasMarshmallow() ? PendingIntent.FLAG_IMMUTABLE : 0);
mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent,
VersionUtils.INSTANCE.hasMarshmallow() ? PendingIntent.FLAG_IMMUTABLE : 0);
mediaSession = new MediaSessionCompat(
this,

View File

@ -15,95 +15,51 @@
package code.name.monkey.retromusic.service.notification
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context.NOTIFICATION_SERVICE
import android.content.pm.ServiceInfo
import android.os.Build
import android.content.Context
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.model.Song
abstract class PlayingNotification {
protected lateinit var service: MusicService
protected var stopped: Boolean = false
private var notifyMode = NOTIFY_MODE_BACKGROUND
private var notificationManager: NotificationManager? = null
abstract class PlayingNotification(context: Context) :
NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) {
abstract fun updateMetadata(song: Song, onUpdate: () -> Unit)
@Synchronized
fun init(service: MusicService) {
this.service = service
notificationManager = service.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
}
}
abstract fun setPlaying(isPlaying: Boolean)
abstract fun update()
@Synchronized
fun stop() {
stopped = true
service.stopForeground(true)
notificationManager!!.cancel(NOTIFICATION_ID)
}
internal fun updateNotifyModeAndPostNotification(notification: Notification) {
val newNotifyMode: Int = if (service.isPlaying) {
NOTIFY_MODE_FOREGROUND
} else {
NOTIFY_MODE_BACKGROUND
}
if (notifyMode != newNotifyMode && newNotifyMode == NOTIFY_MODE_BACKGROUND) {
service.stopForeground(false)
}
if (newNotifyMode == NOTIFY_MODE_FOREGROUND) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
service.startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
)
} else {
service.startForeground(NOTIFICATION_ID,notification)
}
} else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) {
notificationManager!!.notify(NOTIFICATION_ID, notification)
}
notifyMode = newNotifyMode
}
@RequiresApi(26)
private fun createNotificationChannel() {
var notificationChannel: NotificationChannel? = notificationManager!!
.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
if (notificationChannel == null) {
notificationChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
service.getString(R.string.playing_notification_name),
NotificationManager.IMPORTANCE_LOW
)
notificationChannel.description =
service.getString(R.string.playing_notification_description)
notificationChannel.enableLights(false)
notificationChannel.enableVibration(false)
notificationChannel.setShowBadge(false)
notificationManager!!.createNotificationChannel(notificationChannel)
}
}
abstract fun updateFavorite(song: Song, onUpdate: () -> Unit)
companion object {
const val NOTIFICATION_CONTROLS_SIZE_MULTIPLIER = 1.0f
internal const val NOTIFICATION_CHANNEL_ID = "playing_notification"
private const val NOTIFICATION_ID = 1
private const val NOTIFY_MODE_FOREGROUND = 1
private const val NOTIFY_MODE_BACKGROUND = 0
const val NOTIFICATION_ID = 1
@RequiresApi(26)
fun createNotificationChannel(
context: Context,
notificationManager: NotificationManager
) {
var notificationChannel: NotificationChannel? = notificationManager
.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
if (notificationChannel == null) {
notificationChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
context.getString(R.string.playing_notification_name),
NotificationManager.IMPORTANCE_LOW
)
notificationChannel.description =
context.getString(R.string.playing_notification_description)
notificationChannel.enableLights(false)
notificationChannel.enableVibration(false)
notificationChannel.setShowBadge(false)
notificationManager.createNotificationChannel(notificationChannel)
}
}
}
}

View File

@ -14,14 +14,16 @@
package code.name.monkey.retromusic.service.notification
import android.annotation.SuppressLint
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Build
import android.support.v4.media.session.MediaSessionCompat
import androidx.core.app.NotificationCompat
import androidx.core.text.HtmlCompat
import androidx.media.app.NotificationCompat.MediaStyle
@ -33,177 +35,196 @@ import code.name.monkey.retromusic.db.toSongEntity
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.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.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import kotlinx.coroutines.withContext
class PlayingNotificationImpl : PlayingNotification(), KoinComponent {
private var target: Target<BitmapPaletteWrapper>? = null
@SuppressLint("RestrictedApi")
class PlayingNotificationImpl(
val context: Context,
mediaSessionToken: MediaSessionCompat.Token
) : PlayingNotification(context) {
@Synchronized
override fun update() {
stopped = false
GlobalScope.launch {
val song = service.currentSong
init {
val action = Intent(context, MainActivity::class.java)
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val clickIntent =
PendingIntent.getActivity(
context,
0,
action,
PendingIntent.FLAG_UPDATE_CURRENT or if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
val serviceName = ComponentName(context, MusicService::class.java)
val intent = Intent(ACTION_QUIT)
intent.component = serviceName
val deleteIntent = PendingIntent.getService(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or (if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0)
)
val toggleFavorite = buildFavoriteAction(false)
val playPauseAction = buildPlayAction(true)
val previousAction = NotificationCompat.Action(
R.drawable.ic_skip_previous_round_white_32dp,
context.getString(R.string.action_previous),
retrievePlaybackAction(ACTION_REWIND)
)
val nextAction = NotificationCompat.Action(
R.drawable.ic_skip_next_round_white_32dp,
context.getString(R.string.action_next),
retrievePlaybackAction(ACTION_SKIP)
)
val dismissAction = NotificationCompat.Action(
R.drawable.ic_close,
context.getString(R.string.customactivityoncrash_error_activity_error_details_close),
retrievePlaybackAction(ACTION_QUIT)
)
setSmallIcon(R.drawable.ic_notification)
setContentIntent(clickIntent)
setDeleteIntent(deleteIntent)
setShowWhen(false)
addAction(toggleFavorite)
addAction(previousAction)
addAction(playPauseAction)
addAction(nextAction)
addAction(dismissAction)
setStyle(
MediaStyle()
.setMediaSession(mediaSessionToken)
.setShowActionsInCompactView(1, 2, 3)
)
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (Build.VERSION.SDK_INT <=
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
) {
this.color = color
}
}
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
setContentTitle(song.title)
setContentText(
HtmlCompat.fromHtml(
"<b>" + song.albumName + "</b>",
HtmlCompat.FROM_HTML_MODE_LEGACY
)
)
val bigNotificationImageSize = context.resources
.getDimensionPixelSize(R.dimen.notification_big_image_size)
GlideApp.with(context).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
//.checkIgnoreMediaStore()
.centerCrop()
.into(object : CustomTarget<BitmapPaletteWrapper>(
bigNotificationImageSize,
bigNotificationImageSize
) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
setLargeIcon(
resource.bitmap
)
if (Build.VERSION.SDK_INT <=
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
) {
color = RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
}
onUpdate()
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
setLargeIcon(null)
onUpdate()
}
override fun onLoadCleared(placeholder: Drawable?) {
setLargeIcon(null)
onUpdate()
}
})
}
private fun buildPlayAction(isPlaying: Boolean): NotificationCompat.Action {
val playButtonResId =
if (isPlaying) R.drawable.ic_pause_white_48dp else R.drawable.ic_play_arrow_white_48dp
return NotificationCompat.Action.Builder(
playButtonResId,
context.getString(R.string.action_play_pause),
retrievePlaybackAction(ACTION_TOGGLE_PAUSE)
).build()
}
private fun buildFavoriteAction(isFavorite: Boolean): NotificationCompat.Action {
val favoriteResId =
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
return NotificationCompat.Action.Builder(
favoriteResId,
context.getString(R.string.action_toggle_favorite),
retrievePlaybackAction(TOGGLE_FAVORITE)
).build()
}
override fun setPlaying(isPlaying: Boolean) {
mActions[2] = buildPlayAction(isPlaying)
}
override fun updateFavorite(song: Song, onUpdate: () -> Unit) {
GlobalScope.launch(Dispatchers.IO) {
val playlist: PlaylistEntity = MusicUtil.repository.favoritePlaylist()
val isPlaying = service.isPlaying
val isFavorite = if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId)
MusicUtil.repository.isFavoriteSong(songEntity).isNotEmpty()
} else false
val playButtonResId =
if (isPlaying) R.drawable.ic_pause_white_48dp else R.drawable.ic_play_arrow_white_48dp
val favoriteResId =
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
val action = Intent(service, MainActivity::class.java)
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val clickIntent =
PendingIntent.getActivity(
service, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0 or PendingIntent.FLAG_UPDATE_CURRENT
)
val serviceName = ComponentName(service, MusicService::class.java)
val intent = Intent(ACTION_QUIT)
intent.component = serviceName
val deleteIntent = PendingIntent.getService(
service,
0,
intent,
if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0 or PendingIntent.FLAG_UPDATE_CURRENT
)
val bigNotificationImageSize = service.resources
.getDimensionPixelSize(R.dimen.notification_big_image_size)
service.runOnUiThread {
if (target != null) {
Glide.with(service).clear(target)
}
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
//.checkIgnoreMediaStore()
.centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(
bigNotificationImageSize,
bigNotificationImageSize
) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
update(
resource.bitmap,
RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
update(null, Color.TRANSPARENT)
}
fun update(bitmap: Bitmap?, color: Int) {
var bitmapFinal = bitmap
if (bitmapFinal == null) {
bitmapFinal = BitmapFactory.decodeResource(
service.resources,
R.drawable.default_audio_art
)
}
val toggleFavorite = NotificationCompat.Action(
favoriteResId,
service.getString(R.string.action_toggle_favorite),
retrievePlaybackAction(TOGGLE_FAVORITE)
)
val playPauseAction = NotificationCompat.Action(
playButtonResId,
service.getString(R.string.action_play_pause),
retrievePlaybackAction(ACTION_TOGGLE_PAUSE)
)
val previousAction = NotificationCompat.Action(
R.drawable.ic_skip_previous_round_white_32dp,
service.getString(R.string.action_previous),
retrievePlaybackAction(ACTION_REWIND)
)
val nextAction = NotificationCompat.Action(
R.drawable.ic_skip_next_round_white_32dp,
service.getString(R.string.action_next),
retrievePlaybackAction(ACTION_SKIP)
)
val builder = NotificationCompat.Builder(
service,
NOTIFICATION_CHANNEL_ID
)
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(bitmapFinal)
.setContentIntent(clickIntent)
.setDeleteIntent(deleteIntent)
.setContentTitle(
HtmlCompat.fromHtml(
"<b>" + song.title + "</b>",
HtmlCompat.FROM_HTML_MODE_LEGACY
)
)
.setContentText(song.artistName)
.setSubText(
HtmlCompat.fromHtml(
"<b>" + song.albumName + "</b>",
HtmlCompat.FROM_HTML_MODE_LEGACY
)
)
.setOngoing(isPlaying)
.setShowWhen(false)
.addAction(toggleFavorite)
.addAction(previousAction)
.addAction(playPauseAction)
.addAction(nextAction)
builder.setStyle(
MediaStyle()
.setMediaSession(service.mediaSession.sessionToken)
.setShowActionsInCompactView(1, 2, 3)
)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (Build.VERSION.SDK_INT <=
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
) {
builder.color = color
}
if (stopped) {
return // notification has been stopped before loading was finished
}
updateNotifyModeAndPostNotification(builder.build())
}
})
withContext(Dispatchers.Main) {
mActions[0] = buildFavoriteAction(isFavorite)
onUpdate()
}
}
}
private fun retrievePlaybackAction(action: String): PendingIntent {
val serviceName = ComponentName(service, MusicService::class.java)
val serviceName = ComponentName(context, MusicService::class.java)
val intent = Intent(action)
intent.component = serviceName
return PendingIntent.getService(
service, 0, intent,
if (VersionUtils.hasMarshmallow()) PendingIntent.FLAG_IMMUTABLE
else 0 or PendingIntent.FLAG_UPDATE_CURRENT
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or
if (VersionUtils.hasMarshmallow()) PendingIntent.FLAG_IMMUTABLE
else 0
)
}
companion object {
fun from(
context: Context,
notificationManager: NotificationManager,
mediaSession: MediaSessionCompat
): PlayingNotification {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(context, notificationManager)
}
return PlayingNotificationImpl(context, mediaSession.sessionToken)
}
}
}

View File

@ -14,6 +14,8 @@
package code.name.monkey.retromusic.service.notification
import android.annotation.SuppressLint
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
@ -21,6 +23,7 @@ import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Build
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
@ -29,6 +32,7 @@ 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.extensions.surfaceColor
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
@ -39,224 +43,231 @@ import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.RetroUtil.createBitmap
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
/**
* @author Hemanth S (h4h13).
*/
class PlayingNotificationOreo : PlayingNotification() {
@SuppressLint("RestrictedApi")
class PlayingNotificationOreo(
val context: Context
) : PlayingNotification(context) {
private var target: Target<BitmapPaletteWrapper>? = null
private var primaryColor: Int = 0
private fun getCombinedRemoteViews(collapsed: Boolean, song: Song): RemoteViews {
val remoteViews = RemoteViews(
service.packageName,
if (collapsed) R.layout.layout_notification_collapsed else R.layout.layout_notification_expanded
)
remoteViews.setTextViewText(
R.id.appName,
service.getString(R.string.app_name) + "" + song.albumName
)
remoteViews.setTextViewText(R.id.title, song.title)
remoteViews.setTextViewText(R.id.subtitle, song.artistName)
linkButtons(remoteViews)
return remoteViews
}
init {
val notificationLayout = getCombinedRemoteViews(true)
val notificationLayoutBig = getCombinedRemoteViews(false)
override fun update() {
stopped = false
val song = service.currentSong
val isPlaying = service.isPlaying
val notificationLayout = getCombinedRemoteViews(true, song)
val notificationLayoutBig = getCombinedRemoteViews(false, song)
val action = Intent(service, MainActivity::class.java)
val action = Intent(context, MainActivity::class.java)
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val clickIntent = PendingIntent
.getActivity(
service,
context,
0,
action,
PendingIntent.FLAG_UPDATE_CURRENT or if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
val deleteIntent = buildPendingIntent(service, ACTION_QUIT, null)
val deleteIntent = buildPendingIntent(context, ACTION_QUIT, null)
val builder = NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(clickIntent)
.setDeleteIntent(deleteIntent)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCustomContentView(notificationLayout)
.setCustomBigContentView(notificationLayoutBig)
.setOngoing(isPlaying)
val bigNotificationImageSize = service.resources
.getDimensionPixelSize(R.dimen.notification_big_image_size)
service.runOnUiThread {
if (target != null) {
Glide.with(service).clear(target)
}
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
.centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(
bigNotificationImageSize,
bigNotificationImageSize
) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
/* val mediaNotificationProcessor = MediaNotificationProcessor(
service,
service
) { i, _ -> update(resource.bitmap, i) }
mediaNotificationProcessor.processNotification(resource.bitmap)*/
val colors = MediaNotificationProcessor(service, resource.bitmap)
update(resource.bitmap, colors.backgroundColor)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
update(
null,
resolveColor(service, R.attr.colorSurface, Color.WHITE)
)
}
private fun update(bitmap: Bitmap?, bgColor: Int) {
var bgColorFinal = bgColor
if (bitmap != null) {
notificationLayout.setImageViewBitmap(R.id.largeIcon, bitmap)
notificationLayoutBig.setImageViewBitmap(R.id.largeIcon, bitmap)
} else {
notificationLayout.setImageViewResource(
R.id.largeIcon,
R.drawable.default_audio_art
)
notificationLayoutBig.setImageViewResource(
R.id.largeIcon,
R.drawable.default_audio_art
)
}
// Android 12 applies a standard Notification template to every notification
// which will in turn have a default background so setting a different background
// than that, looks weird
if (!VersionUtils.hasS()) {
if (!PreferenceUtil.isColoredNotification) {
bgColorFinal =
resolveColor(service, R.attr.colorPrimary, Color.WHITE)
}
setBackgroundColor(bgColorFinal)
}
setNotificationContent(ColorUtil.isColorLight(bgColorFinal))
if (stopped) {
return // notification has been stopped before loading was finished
}
updateNotifyModeAndPostNotification(builder.build())
}
private fun setBackgroundColor(color: Int) {
notificationLayout.setInt(R.id.image, "setBackgroundColor", color)
notificationLayoutBig.setInt(R.id.image, "setBackgroundColor", color)
}
private fun setNotificationContent(dark: Boolean) {
val primary = MaterialValueHelper.getPrimaryTextColor(service, dark)
val secondary = MaterialValueHelper.getSecondaryTextColor(service, dark)
val close = createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_close,
primary
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val prev = createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_skip_previous_round_white_32dp,
primary
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val next = createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_skip_next_round_white_32dp,
primary
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val playPause = createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
if (isPlaying)
R.drawable.ic_pause_white_48dp
else
R.drawable.ic_play_arrow_white_48dp, primary
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
notificationLayout.setTextColor(R.id.title, primary)
notificationLayout.setTextColor(R.id.subtitle, secondary)
notificationLayout.setTextColor(R.id.appName, secondary)
notificationLayout.setImageViewBitmap(R.id.action_prev, prev)
notificationLayout.setImageViewBitmap(R.id.action_next, next)
notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause)
notificationLayoutBig.setTextColor(R.id.title, primary)
notificationLayoutBig.setTextColor(R.id.subtitle, secondary)
notificationLayoutBig.setTextColor(R.id.appName, secondary)
notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close)
notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev)
notificationLayoutBig.setImageViewBitmap(R.id.action_next, next)
notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause)
notificationLayout.setImageViewBitmap(
R.id.smallIcon,
createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_notification,
secondary
), 0.6f
)
)
notificationLayoutBig.setImageViewBitmap(
R.id.smallIcon,
createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
R.drawable.ic_notification,
secondary
), 0.6f
)
)
}
})
}
if (stopped) {
return // notification has been stopped before loading was finished
}
updateNotifyModeAndPostNotification(builder.build())
setSmallIcon(R.drawable.ic_notification)
setContentIntent(clickIntent)
setDeleteIntent(deleteIntent)
setCategory(NotificationCompat.CATEGORY_SERVICE)
priority = NotificationCompat.PRIORITY_MAX
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
setCustomContentView(notificationLayout)
setCustomBigContentView(notificationLayoutBig)
setOngoing(true)
}
private fun getCombinedRemoteViews(collapsed: Boolean): RemoteViews {
val remoteViews = RemoteViews(
context.packageName,
if (collapsed) R.layout.layout_notification_collapsed else R.layout.layout_notification_expanded
)
linkButtons(remoteViews)
return remoteViews
}
@SuppressLint("RestrictedApi")
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
val bigNotificationImageSize = context.resources
.getDimensionPixelSize(R.dimen.notification_big_image_size)
GlideApp.with(context).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
.centerCrop()
.into(object : CustomTarget<BitmapPaletteWrapper>(
bigNotificationImageSize,
bigNotificationImageSize
) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
val colors = MediaNotificationProcessor(context, resource.bitmap)
update(resource.bitmap, colors.backgroundColor)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
update(
null,
resolveColor(context, R.attr.colorSurface, Color.WHITE)
)
}
override fun onLoadCleared(placeholder: Drawable?) {
}
private fun update(bitmap: Bitmap?, bgColor: Int) {
var bgColorFinal = bgColor
if (bitmap != null) {
contentView.setImageViewBitmap(R.id.largeIcon, bitmap)
bigContentView.setImageViewBitmap(R.id.largeIcon, bitmap)
} else {
contentView.setImageViewResource(
R.id.largeIcon,
R.drawable.default_audio_art
)
bigContentView.setImageViewResource(
R.id.largeIcon,
R.drawable.default_audio_art
)
}
// Android 12 applies a standard Notification template to every notification
// which will in turn have a default background so setting a different background
// than that, looks weird
if (!VersionUtils.hasS()) {
if (!PreferenceUtil.isColoredNotification) {
bgColorFinal =
resolveColor(context, R.attr.colorSurface, Color.WHITE)
}
setBackgroundColor(bgColorFinal)
setNotificationContent(ColorUtil.isColorLight(bgColorFinal))
}else {
setNotificationContent(!ColorUtil.isColorLight(context.surfaceColor()))
}
onUpdate()
}
private fun setBackgroundColor(color: Int) {
contentView.setInt(R.id.image, "setBackgroundColor", color)
bigContentView.setInt(R.id.image, "setBackgroundColor", color)
}
private fun setNotificationContent(dark: Boolean) {
val primary = MaterialValueHelper.getPrimaryTextColor(context, dark)
val secondary = MaterialValueHelper.getSecondaryTextColor(context, dark)
primaryColor = primary
val close = createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_close,
primary
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val prev = createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_skip_previous_round_white_32dp,
primary
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val next = createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_skip_next_round_white_32dp,
primary
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER
)
val playPause = getPlayPauseBitmap(true)
contentView.setTextColor(R.id.title, primary)
contentView.setTextColor(R.id.subtitle, secondary)
contentView.setTextColor(R.id.appName, secondary)
contentView.setImageViewBitmap(R.id.action_prev, prev)
contentView.setImageViewBitmap(R.id.action_next, next)
contentView.setImageViewBitmap(R.id.action_play_pause, playPause)
contentView.setTextViewText(
R.id.appName,
context.getString(R.string.app_name) + "" + song.albumName
)
contentView.setTextViewText(R.id.title, song.title)
contentView.setTextViewText(R.id.subtitle, song.artistName)
bigContentView.setTextColor(R.id.title, primary)
bigContentView.setTextColor(R.id.subtitle, secondary)
bigContentView.setTextColor(R.id.appName, secondary)
bigContentView.setImageViewBitmap(R.id.action_quit, close)
bigContentView.setImageViewBitmap(R.id.action_prev, prev)
bigContentView.setImageViewBitmap(R.id.action_next, next)
bigContentView.setImageViewBitmap(R.id.action_play_pause, playPause)
bigContentView.setTextViewText(
R.id.appName,
context.getString(R.string.app_name) + "" + song.albumName
)
bigContentView.setTextViewText(R.id.title, song.title)
bigContentView.setTextViewText(R.id.subtitle, song.artistName)
contentView.setImageViewBitmap(
R.id.smallIcon,
createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_notification,
secondary
), 0.6f
)
)
bigContentView.setImageViewBitmap(
R.id.smallIcon,
createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_notification,
secondary
), 0.6f
)
)
}
})
}
private fun getPlayPauseBitmap(isPlaying: Boolean): Bitmap {
return createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
if (isPlaying)
R.drawable.ic_pause_white_48dp
else
R.drawable.ic_play_arrow_white_48dp, primaryColor
), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER)
}
override fun setPlaying(isPlaying: Boolean) {
getPlayPauseBitmap(isPlaying).also {
contentView.setImageViewBitmap(R.id.action_play_pause, it)
bigContentView.setImageViewBitmap(R.id.action_play_pause, it)
}
}
override fun updateFavorite(song: Song, onUpdate: () -> Unit) {
}
private fun buildPendingIntent(
context: Context, action: String,
@ -275,23 +286,34 @@ class PlayingNotificationOreo : PlayingNotification() {
private fun linkButtons(notificationLayout: RemoteViews) {
var pendingIntent: PendingIntent
val serviceName = ComponentName(service, MusicService::class.java)
val serviceName = ComponentName(context, MusicService::class.java)
// Previous track
pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName)
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName)
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent)
// Next track
pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName)
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent)
// Close
pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName)
pendingIntent = buildPendingIntent(context, ACTION_QUIT, serviceName)
notificationLayout.setOnClickPendingIntent(R.id.action_quit, pendingIntent)
}
companion object {
fun from(
context: Context,
notificationManager: NotificationManager
): PlayingNotification {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(context, notificationManager)
}
return PlayingNotificationOreo(context)
}
}
}

View File

@ -241,7 +241,7 @@ object MusicUtil : KoinComponent {
var musicMediaTitle = mediaTitle
return try {
if (TextUtils.isEmpty(musicMediaTitle)) {
return ""
return "-"
}
musicMediaTitle = musicMediaTitle!!.trim { it <= ' ' }.lowercase()
if (musicMediaTitle.startsWith("the ")) {
@ -526,6 +526,7 @@ object MusicUtil : KoinComponent {
@RequiresApi(Build.VERSION_CODES.R)
fun deleteTracksR(activity: Activity, songs: List<Song>) {
removeFromQueue(songs)
val pendingIntent = MediaStore.createDeleteRequest(activity.contentResolver, songs.map {
getSongFileUri(it.id)
})

View File

@ -372,7 +372,7 @@ object PreferenceUtil {
var artistGridStyle: GridStyle
get() {
val id: Int = sharedPreferences.getInt(ARTIST_GRID_STYLE, 4)
val id: Int = sharedPreferences.getInt(ARTIST_GRID_STYLE, 3)
return GridStyle.values().firstOrNull { gridStyle ->
gridStyle.id == id
} ?: GridStyle.Circular

View File

@ -473,4 +473,12 @@ public class MediaNotificationProcessor {
public interface OnPaletteLoadedListener {
void onPaletteLoaded(MediaNotificationProcessor mediaNotificationProcessor);
}
public static MediaNotificationProcessor errorColor(Context context) {
MediaNotificationProcessor errorColors = new MediaNotificationProcessor(context);
errorColors.backgroundColor = 0x15724528;
errorColors.primaryTextColor = 0x6974059;
errorColors.secondaryTextColor = 0x8684677;
return errorColors;
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
Background for widgets to make the rounded corners based on the
appWidgetRadius attribute value
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="16dp" />
<solid android:color="?android:attr/colorBackground" />
</shape>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="@dimen/app_widget_md3_height"
android:background="@drawable/app_widget_background"
android:backgroundTint="?android:attr/colorBackground"
android:orientation="horizontal"
tools:ignore="ContentDescription">
<ImageView
android:id="@+id/image"
android:layout_width="@dimen/app_widget_md3_image_size"
android:layout_height="@dimen/app_widget_md3_image_size"
android:background="@drawable/app_widget_background"
android:layout_margin="8dp"
android:layout_gravity="center_vertical"
android:scaleType="centerCrop" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/media_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
android:layoutDirection="ltr"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp">
<ImageButton
android:id="@+id/button_prev"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/widget_selector"
tools:src="@drawable/ic_skip_previous"
tools:tint="@color/ate_secondary_text_dark" />
<ImageButton
android:id="@+id/button_toggle_play_pause"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/widget_selector"
tools:src="@drawable/ic_play_arrow_white_32dp"
tools:tint="@color/ate_secondary_text_dark" />
<ImageButton
android:id="@+id/button_next"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/widget_selector"
tools:src="@drawable/ic_skip_next"
tools:tint="@color/ate_secondary_text_dark" />
</LinearLayout>
<LinearLayout
android:id="@+id/media_titles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/media_actions"
android:layout_alignParentTop="true"
android:orientation="vertical"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextViewSubtitle1"
android:textColor="?android:attr/textColorPrimary"
tools:text="Title" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingTop="4dp"
android:singleLine="true"
android:textAppearance="@style/TextViewNormal"
android:textColor="?android:attr/textColorSecondary"
tools:text="Text" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>

View File

@ -5,5 +5,5 @@
android:id="@+id/action_search"
android:icon="@drawable/ic_search"
android:title="@string/action_search"
app:showAsAction="ifRoom" />
app:showAsAction="always" />
</menu>

View File

@ -39,6 +39,9 @@
<dimen name="app_widget_card_radius">2dp</dimen>
<dimen name="now_playing_top_margin">12dp</dimen>
<dimen name="app_widget_md3_height">96dp</dimen>
<dimen name="app_widget_md3_image_size">80dp</dimen>
<dimen name="icon_notification_dimen">32dp</dimen>
<dimen name="toolbar_margin_horizontal">8dp</dimen>
<dimen name="toolbar_height">48dp</dimen>

View File

@ -454,6 +454,7 @@
<string name="sort_order_a_z">Ascending</string>
<string name="sort_order_album">Album</string>
<string name="sort_order_artist">Artist</string>
<string name="sort_order_album_artist">@string/album_artist</string>
<string name="sort_order_composer">Composer</string>
<string name="sort_order_date">Date added</string>
<string name="sort_order_date_modified">Date modified</string>
@ -517,4 +518,5 @@
<string name="restore_message">Do you want to restore backup?</string>
<string name="title_new_backup">New Backup</string>
<string name="backup_restore_settings_summary">Backup and restore your settings, playlists</string>
<string name="app_widget_md3_name">MD3</string>
</resources>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:initialLayout="@layout/app_widget_classic"
android:minWidth="@dimen/app_widget_classic_min_width"
android:minHeight="@dimen/app_widget_classic_min_height"
android:previewImage="@drawable/widget_classic"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="0"
android:widgetCategory="keyguard|home_screen"
tools:ignore="UnusedAttribute" />

View File

@ -25,7 +25,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'com.google.android.material:material:1.5.0-beta01'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.preference:preference-ktx:1.2.0-beta01'
implementation 'androidx.cardview:cardview:1.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

View File

@ -1,9 +1,9 @@
package code.name.monkey.appthemehelper
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.graphics.Color
import android.os.Build
import android.view.View
import androidx.annotation.ColorInt
@ -18,7 +18,6 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
*/
object ATH {
@SuppressLint("CommitPrefEdits")
fun didThemeValuesChange(context: Context, since: Long): Boolean {
return ThemeStore.isConfigured(context) && ThemeStore.prefs(context).getLong(
ThemeStorePrefKeys.VALUES_CHANGED,
@ -26,7 +25,7 @@ object ATH {
) > since
}
fun setLightStatusbar(activity: Activity, enabled: Boolean) {
fun setLightStatusBar(activity: Activity, enabled: Boolean) {
activity.window.apply {
WindowInsetsControllerCompat(
this,
@ -36,29 +35,26 @@ object ATH {
}
fun setLightNavigationbar(activity: Activity, enabled: Boolean) {
activity.window?.apply {
activity.window.apply {
WindowInsetsControllerCompat(
this,
decorView
).isAppearanceLightNavigationBars = enabled
navigationBarColor = Color.TRANSPARENT
}
}
fun setLightNavigationbarAuto(activity: Activity, bgColor: Int) {
fun setLightNavigationBarAuto(activity: Activity, bgColor: Int) {
setLightNavigationbar(activity, ColorUtil.isColorLight(bgColor))
}
fun setNavigationbarColorAuto(activity: Activity) {
setNavigationbarColor(activity, ThemeStore.navigationBarColor(activity))
}
fun setNavigationbarColor(activity: Activity, color: Int) {
fun setNavigationBarColor(activity: Activity, color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
activity.window.navigationBarColor = color
} else {
activity.window.navigationBarColor = ColorUtil.darkenColor(color)
}
setLightNavigationbarAuto(activity, color)
setLightNavigationBarAuto(activity, color)
}
fun setActivityToolbarColorAuto(activity: Activity, toolbar: Toolbar?) {

View File

@ -43,9 +43,9 @@ class ATEColorPreference @JvmOverloads constructor(
invalidateColor()
}*/
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
mView = holder?.itemView
mView = holder.itemView
invalidateColor()
}

View File

@ -22,7 +22,7 @@ import androidx.preference.PreferenceViewHolder
import code.name.monkey.appthemehelper.ThemeStore
class ATEPreferenceCategory @JvmOverloads constructor(
context: Context?,
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int = -1,
defStyleRes: Int = -1

View File

@ -9,9 +9,9 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:7.0.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
def nav_version = "2.3.5"
def nav_version = "2.4.0-beta02"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
classpath "com.diffplug.spotless:spotless-plugin-gradle:5.16.0"
classpath "com.diffplug.spotless:spotless-plugin-gradle:6.0.1"
}
}