Merge dev into l10n_dev

This commit is contained in:
Prathamesh More 2022-01-27 15:30:05 +05:30
commit 8425bec797
402 changed files with 3815 additions and 26546 deletions

View file

@ -14,22 +14,28 @@
## Screenshots ## Screenshots
### App Themes ### App Themes
| <img src="screenshots/home.jpeg" width="200"/> | <img src="screenshots/home_dark.jpeg" width="200"/> | <img src="screenshots/home_black.jpeg" width="200"/> | | <img src="screenshots/home_light.jpg" width="200"/> | <img src="screenshots/home_dark.jpg" width="200"/> | <img src="screenshots/home_black.jpg" width="200"/> |
|:---:|:---:|:---:| |:---:|:---:|:---:|
|Clearly white| Kinda dark | Just black| |Clearly white| Kinda dark | Just black|
### Player screen ### Player screen
| <img src="screenshots/home.jpeg" width="200"/>| <img src="screenshots/list.jpeg" width="200"/>| <img src="screenshots/albums.jpeg" width="200"/>| <img src="screenshots/settings.jpeg" width="200"/>| | <img src="screenshots/home_light.jpg" width="200"/>| <img src="screenshots/songs.jpg" width="200"/>| <img src="screenshots/albums.jpg" width="200"/>| <img src="screenshots/artists.jpg" width="200"/>| <img src="screenshots/settings.jpg" width="200"/>|
|:---:|:---:|:---:|:---:| |:---:|:---:|:---:|:---:|:---:|
| Home | Songs | Albums | Settings | | Home | Songs | Albums | Artists | Settings |
### Synced lyrics screen
| <img src="screenshots/synced_light.jpg" width="200"/>| <img src="screenshots/synced_dark.jpg" width="200"/>| <img src="screenshots/synced_black.jpg" width="200"/>|
|:---:|:---:|:---:|
| Synced light | Synced dark | Synced black |
### 9+ Now playing themes ### 9+ Now playing themes
| <img src="screenshots/np_normal.jpeg" width="200"/> |<img src="screenshots/np_fit.jpeg" width="200"/>| <img src="screenshots/np_flat.jpeg" width="200"/> | <img src="screenshots/np_color.jpeg" width="200"/> | <img src="screenshots/np_material.jpeg" width="200"/> | | <img src="screenshots/normal.jpg" width="200"/> |<img src="screenshots/fit.jpg" width="200"/>| <img src="screenshots/flat.jpg" width="200"/> | <img src="screenshots/color.jpg" width="200"/> | <img src="screenshots/material.jpg" width="200"/> |
|:-----: |:-----: |:-----: |:-----: |:-----: | |:-----: |:-----: |:-----: |:-----: |:-----: |
| Normal | Fit | Flat | Color | Material | | Normal | Fit | Flat | Color | Material |
| <img src="screenshots/no_classic.jpeg" width="200"/> |<img src="screenshots/np_adaptive.jpeg" width="200"/>| <img src="screenshots/np_blur.jpeg" width="200"/> | <img src="screenshots/np_tiny.jpeg" width="200"/> | <img src="screenshots/np_peak.jpeg" width="200"/> | | <img src="screenshots/classic.jpg" width="200"/> |<img src="screenshots/adaptive.jpg" width="200"/>| <img src="screenshots/blur.jpg" width="200"/> | <img src="screenshots/tiny.jpg" width="200"/> | <img src="screenshots/peak.jpg" width="200"/> |
|:-----: |:-----: |:-----: |:-----: |:-----: | |:-----: |:-----: |:-----: |:-----: |:-----: |
| Classic | Adaptive | Blur | Tiny | Peak | | Classic | Adaptive | Blur | Tiny | Peak |

View file

@ -14,8 +14,8 @@ android {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic" applicationId "code.name.monkey.retromusic"
versionCode 10556 versionCode 10563
versionName '5.6.1' versionName '5.7.0'
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"") buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
} }
@ -57,6 +57,8 @@ android {
abortOnError false abortOnError false
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
@ -86,14 +88,13 @@ static def getDate() {
new Date().format('MMddyyyyss') new Date().format('MMddyyyyss')
} }
dependencies { dependencies {
implementation project(':appthemehelper') implementation project(':appthemehelper')
implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.cardview:cardview:1.0.0"
implementation 'androidx.appcompat:appcompat:1.4.0' implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation 'androidx.annotation:annotation:1.3.0' implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01' implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01'
implementation "androidx.preference:preference-ktx:$preference_version" implementation "androidx.preference:preference-ktx:$preference_version"
implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.core:core-ktx:1.7.0'
@ -101,7 +102,7 @@ dependencies {
//Cast Dependencies //Cast Dependencies
implementation 'androidx.mediarouter:mediarouter:1.2.5' implementation 'androidx.mediarouter:mediarouter:1.2.5'
implementation 'com.google.android.gms:play-services-cast-framework:21.0.0' implementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
//WebServer by NanoHttpd //WebServer by NanoHttpd
implementation "org.nanohttpd:nanohttpd:2.3.1" implementation "org.nanohttpd:nanohttpd:2.3.1"
@ -109,7 +110,7 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version" implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version" implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
def room_version = '2.4.0' def room_version = '2.4.1'
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version" implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"
@ -141,7 +142,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
def koin_version = '3.1.4' def koin_version = '3.1.5'
implementation "io.insert-koin:koin-core:$koin_version" implementation "io.insert-koin:koin-core:$koin_version"
implementation "io.insert-koin:koin-android:$koin_version" implementation "io.insert-koin:koin-android:$koin_version"
@ -149,6 +150,8 @@ dependencies {
kapt 'com.github.bumptech.glide:compiler:4.12.0' kapt 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.12.0' implementation 'com.github.bumptech.glide:okhttp3-integration:4.12.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0' implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0'
@ -156,6 +159,8 @@ dependencies {
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC3' implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC3'
implementation 'com.github.jetradarmobile:android-snowfall:1.2.1' implementation 'com.github.jetradarmobile:android-snowfall:1.2.1'
implementation "dev.chrisbanes.insetter:insetter:0.6.1"
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5' implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
implementation 'com.github.Adonai:jaudiotagger:2.3.15' implementation 'com.github.Adonai:jaudiotagger:2.3.15'
implementation 'com.anjlab.android.iab.v3:library:2.0.3' implementation 'com.anjlab.android.iab.v3:library:2.0.3'

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name" translatable="false">Retro Music-Debug</string>
<bool name="md3_available">true</bool> <bool name="md3_available">true</bool>
<bool name="allowBackup">false</bool> <bool name="allowBackup">false</bool>
</resources> </resources>

View file

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

View file

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

View file

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

View file

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

View file

@ -73,7 +73,7 @@ const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
const val CAROUSEL_EFFECT = "carousel_effect" const val CAROUSEL_EFFECT = "carousel_effect"
const val COLORED_NOTIFICATION = "colored_notification" const val COLORED_NOTIFICATION = "colored_notification"
const val CLASSIC_NOTIFICATION = "classic_notification" const val CLASSIC_NOTIFICATION = "classic_notification"
const val GAP_LESS_PLAYBACK = "gap_less_playback" const val GAP_LESS_PLAYBACK = "gapless_playback"
const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen" const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen"
const val BLURRED_ALBUM_ART = "blurred_album_art" const val BLURRED_ALBUM_ART = "blurred_album_art"
const val NEW_BLUR_AMOUNT = "new_blur_amount" const val NEW_BLUR_AMOUNT = "new_blur_amount"
@ -135,6 +135,7 @@ const val LOCK_SCREEN = "lock_screen"
const val ALBUM_ARTISTS_ONLY = "album_artists_only" const val ALBUM_ARTISTS_ONLY = "album_artists_only"
const val ALBUM_ARTIST = "album_artist" const val ALBUM_ARTIST = "album_artist"
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order" const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
const val ARTIST_DETAIL_SONG_SORT_ORDER = "artist_detail_song_sort_order"
const val LYRICS_OPTIONS = "lyrics_tab_position" const val LYRICS_OPTIONS = "lyrics_tab_position"
const val CHOOSE_EQUALIZER = "choose_equalizer" const val CHOOSE_EQUALIZER = "choose_equalizer"
const val EQUALIZER = "equalizer" const val EQUALIZER = "equalizer"
@ -153,3 +154,11 @@ const val LAST_USED_TAB = "last_used_tab"
const val WHITELIST_MUSIC = "whitelist_music" const val WHITELIST_MUSIC = "whitelist_music"
const val MATERIAL_YOU = "material_you" const val MATERIAL_YOU = "material_you"
const val SNOWFALL = "snowfall" const val SNOWFALL = "snowfall"
const val LYRICS_TYPE = "lyrics_type"
const val PLAYBACK_SPEED = "playback_speed"
const val PLAYBACK_PITCH = "playback_pitch"
const val CUSTOM_FONT = "custom_font"
const val APPBAR_MODE = "appbar_mode"
const val WALLPAPER_ACCENT = "wallpaper_accent"
const val SCREEN_ON_LYRICS = "screen_on_lyrics"
const val CIRCLE_PLAY_BUTTON = "circle_play_button"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,144 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.adapter;
import android.annotation.SuppressLint;
import android.content.res.ColorStateList;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.checkbox.MaterialCheckBox;
import java.util.List;
import code.name.monkey.appthemehelper.ThemeStore;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.model.CategoryInfo;
import code.name.monkey.retromusic.util.SwipeAndDragHelper;
public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder>
implements SwipeAndDragHelper.ActionCompletionContract {
private List<CategoryInfo> categoryInfos;
private final ItemTouchHelper touchHelper;
public CategoryInfoAdapter() {
SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this);
touchHelper = new ItemTouchHelper(swipeAndDragHelper);
}
public void attachToRecyclerView(RecyclerView recyclerView) {
touchHelper.attachToRecyclerView(recyclerView);
}
@NonNull
public List<CategoryInfo> getCategoryInfos() {
return categoryInfos;
}
public void setCategoryInfos(@NonNull List<CategoryInfo> categoryInfos) {
this.categoryInfos = categoryInfos;
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return categoryInfos.size();
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) {
CategoryInfo categoryInfo = categoryInfos.get(position);
holder.checkBox.setChecked(categoryInfo.isVisible());
holder.title.setText(
holder.title.getResources().getString(categoryInfo.getCategory().getStringRes()));
holder.itemView.setOnClickListener(
v -> {
if (!(categoryInfo.isVisible() && isLastCheckedCategory(categoryInfo))) {
categoryInfo.setVisible(!categoryInfo.isVisible());
holder.checkBox.setChecked(categoryInfo.isVisible());
} else {
Toast.makeText(
holder.itemView.getContext(),
R.string.you_have_to_select_at_least_one_category,
Toast.LENGTH_SHORT)
.show();
}
});
holder.dragView.setOnTouchListener(
(view, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
touchHelper.startDrag(holder);
}
return false;
});
}
@Override
@NonNull
public CategoryInfoAdapter.ViewHolder onCreateViewHolder(
@NonNull ViewGroup parent, int viewType) {
View view =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.preference_dialog_library_categories_listitem, parent, false);
return new ViewHolder(view);
}
@Override
public void onViewMoved(int oldPosition, int newPosition) {
CategoryInfo categoryInfo = categoryInfos.get(oldPosition);
categoryInfos.remove(oldPosition);
categoryInfos.add(newPosition, categoryInfo);
notifyItemMoved(oldPosition, newPosition);
}
private boolean isLastCheckedCategory(CategoryInfo categoryInfo) {
if (categoryInfo.isVisible()) {
for (CategoryInfo c : categoryInfos) {
if (c != categoryInfo && c.isVisible()) {
return false;
}
}
}
return true;
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final MaterialCheckBox checkBox;
private final View dragView;
private final TextView title;
ViewHolder(View view) {
super(view);
checkBox = view.findViewById(R.id.checkbox);
checkBox.setButtonTintList(
ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext())));
title = view.findViewById(R.id.title);
dragView = view.findViewById(R.id.drag_view);
}
}
}

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.adapter
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.PreferenceDialogLibraryCategoriesListitemBinding
import code.name.monkey.retromusic.model.CategoryInfo
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.SwipeAndDragHelper
import code.name.monkey.retromusic.util.SwipeAndDragHelper.ActionCompletionContract
class CategoryInfoAdapter : RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder>(),
ActionCompletionContract {
var categoryInfos: MutableList<CategoryInfo> =
PreferenceUtil.libraryCategory.toMutableList()
@SuppressLint("NotifyDataSetChanged")
set(value) {
field = value
notifyDataSetChanged()
}
private val touchHelper: ItemTouchHelper
fun attachToRecyclerView(recyclerView: RecyclerView?) {
touchHelper.attachToRecyclerView(recyclerView)
}
override fun getItemCount(): Int {
return categoryInfos.size
}
@SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val categoryInfo = categoryInfos[position]
holder.binding.checkbox.isChecked = categoryInfo.visible
holder.binding.title.text =
holder.binding.title.resources.getString(categoryInfo.category.stringRes)
holder.itemView.setOnClickListener {
if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) {
categoryInfo.visible = !categoryInfo.visible
holder.binding.checkbox.isChecked = categoryInfo.visible
} else {
Toast.makeText(
holder.itemView.context,
R.string.you_have_to_select_at_least_one_category,
Toast.LENGTH_SHORT
)
.show()
}
}
holder.binding.dragView.setOnTouchListener { _: View?, event: MotionEvent ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
touchHelper.startDrag(holder)
}
false
}
}
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): ViewHolder {
return ViewHolder(
PreferenceDialogLibraryCategoriesListitemBinding.inflate(
LayoutInflater.from(
parent.context
), parent, false
)
)
}
override fun onViewMoved(oldPosition: Int, newPosition: Int) {
val categoryInfo = categoryInfos[oldPosition]
categoryInfos.removeAt(oldPosition)
categoryInfos.add(newPosition, categoryInfo)
notifyItemMoved(oldPosition, newPosition)
}
private fun isLastCheckedCategory(categoryInfo: CategoryInfo): Boolean {
if (categoryInfo.visible) {
for (c in categoryInfos) {
if (c !== categoryInfo && c.visible) {
return false
}
}
}
return true
}
class ViewHolder(val binding: PreferenceDialogLibraryCategoriesListitemBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.checkbox.buttonTintList =
ColorStateList.valueOf(accentColor(binding.checkbox.context))
}
}
init {
val swipeAndDragHelper = SwipeAndDragHelper(this)
touchHelper = ItemTouchHelper(swipeAndDragHelper)
}
}

View file

@ -14,6 +14,7 @@
*/ */
package code.name.monkey.retromusic.adapter package code.name.monkey.retromusic.adapter
import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -21,7 +22,7 @@ import android.view.ViewOutlineProvider
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder import code.name.monkey.retromusic.databinding.ItemGenreBinding
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
@ -38,7 +39,6 @@ import java.util.*
class GenreAdapter( class GenreAdapter(
private val activity: FragmentActivity, private val activity: FragmentActivity,
var dataSet: List<Genre>, var dataSet: List<Genre>,
private val mItemLayoutRes: Int,
private val listener: IGenreClickListener private val listener: IGenreClickListener
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() { ) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
@ -51,13 +51,13 @@ class GenreAdapter(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false)) return ViewHolder(ItemGenreBinding.inflate(LayoutInflater.from(activity), parent, false))
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val genre = dataSet[position] val genre = dataSet[position]
holder.title?.text = genre.name holder.binding.title.text = genre.name
holder.text?.text = String.format( holder.binding.text.text = String.format(
Locale.getDefault(), Locale.getDefault(),
"%d %s", "%d %s",
genre.songCount, genre.songCount,
@ -72,33 +72,39 @@ class GenreAdapter(
.asBitmapPalette() .asBitmapPalette()
.load(RetroGlideExtension.getSongModel(genreSong)) .load(RetroGlideExtension.getSongModel(genreSong))
.songCoverOptions(genreSong) .songCoverOptions(genreSong)
.into(object : RetroMusicColoredTarget(holder.image!!) { .into(object : RetroMusicColoredTarget(holder.binding.image) {
override fun onColorReady(colors: MediaNotificationProcessor) { override fun onColorReady(colors: MediaNotificationProcessor) {
setColors(holder, colors) setColors(holder, colors)
} }
}) })
// Just for a bit of shadow around image // Just for a bit of shadow around image
holder.image?.outlineProvider = ViewOutlineProvider.BOUNDS holder.binding.image.outlineProvider = ViewOutlineProvider.BOUNDS
} }
private fun setColors(holder: ViewHolder, color: MediaNotificationProcessor) { private fun setColors(holder: ViewHolder, color: MediaNotificationProcessor) {
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor) holder.binding.imageContainerCard.setCardBackgroundColor(color.backgroundColor)
holder.title?.setTextColor(color.primaryTextColor) holder.binding.title.setTextColor(color.primaryTextColor)
holder.text?.setTextColor(color.secondaryTextColor) holder.binding.text.setTextColor(color.secondaryTextColor)
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return dataSet.size return dataSet.size
} }
@SuppressLint("NotifyDataSetChanged")
fun swapDataSet(list: List<Genre>) { fun swapDataSet(list: List<Genre>) {
dataSet = list dataSet = list
notifyDataSetChanged() notifyDataSetChanged()
} }
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { inner class ViewHolder(val binding: ItemGenreBinding) : RecyclerView.ViewHolder(binding.root),
View.OnClickListener {
override fun onClick(v: View?) { override fun onClick(v: View?) {
listener.onClickGenre(dataSet[layoutPosition], itemView) listener.onClickGenre(dataSet[layoutPosition], itemView)
} }
init {
itemView.setOnClickListener(this)
}
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,15 +1,14 @@
package code.name.monkey.retromusic.adapter.backup package code.name.monkey.retromusic.adapter.backup
import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.ItemListBackupBinding
import java.io.File import java.io.File
@ -21,27 +20,27 @@ class BackupAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder( return ViewHolder(
LayoutInflater.from(activity).inflate(R.layout.item_list_card, parent, false) ItemListBackupBinding.inflate(LayoutInflater.from(activity), parent, false)
) )
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.title.text = dataSet[position].nameWithoutExtension holder.binding.title.text = dataSet[position].nameWithoutExtension
} }
override fun getItemCount(): Int = dataSet.size override fun getItemCount(): Int = dataSet.size
@SuppressLint("NotifyDataSetChanged")
fun swapDataset(dataSet: List<File>) { fun swapDataset(dataSet: List<File>) {
this.dataSet = ArrayList(dataSet) this.dataSet = ArrayList(dataSet)
notifyDataSetChanged() notifyDataSetChanged()
} }
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class ViewHolder(val binding: ItemListBackupBinding) :
val title: TextView = itemView.findViewById(R.id.title) RecyclerView.ViewHolder(binding.root) {
val menu: AppCompatImageView = itemView.findViewById(R.id.menu)
init { init {
menu.setOnClickListener { view -> binding.menu.setOnClickListener { view ->
val popupMenu = PopupMenu(activity, view) val popupMenu = PopupMenu(activity, view)
popupMenu.inflate(R.menu.menu_backup) popupMenu.inflate(R.menu.menu_backup)
popupMenu.setOnMenuItemClickListener { menuItem -> popupMenu.setOnMenuItemClickListener { menuItem ->

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,221 @@
/*
* Copyright (c) 2020 Hemanth Savarla.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*/
package code.name.monkey.retromusic.appwidgets
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.widget.RemoteViews
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.service.MusicService.ACTION_TOGGLE_PAUSE
import code.name.monkey.retromusic.service.MusicService.TOGGLE_FAVORITE
import code.name.monkey.retromusic.util.ImageUtil
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
class AppWidgetCircle : BaseAppWidget() {
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
/**
* Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running.
*/
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_circle)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_play_arrow,
secondaryColor
), 1f
)
)
linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView)
}
/**
* Update all active widget instances by pushing changes
*/
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_circle)
val isPlaying = service.isPlaying
val song = service.currentSong
// Set correct drawable for pause state
val playPauseRes =
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play_arrow
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
playPauseRes,
MaterialValueHelper.getSecondaryTextColor(service, true)
), 1f
)
)
val isFavorite = runBlocking(Dispatchers.IO) {
return@runBlocking MusicUtil.repository.isSongFavorite(song.id)
}
val favoriteRes =
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
appWidgetView.setImageViewBitmap(
R.id.button_toggle_favorite, createBitmap(
RetroUtil.getTintedVectorDrawable(
service,
favoriteRes,
MaterialValueHelper.getSecondaryTextColor(service, true)
), 1f
)
)
// Link actions buttons to intents
linkButtons(service, appWidgetView)
if (imageSize == 0) {
val p = RetroUtil.getScreenSize(service)
imageSize = p.x.coerceAtMost(p.y)
}
// Load the album cover async and push the update on completion
service.runOnUiThread {
if (target != null) {
Glide.with(service).clear(target)
}
target = GlideApp.with(service).asBitmapPalette().songCoverOptions(song)
.load(RetroGlideExtension.getSongModel(song))
.apply(
RequestOptions().transform(RoundedCorners(imageSize / 2))
)
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
val palette = resource.palette
update(
resource.bitmap, palette.getVibrantColor(
palette.getMutedColor(
MaterialValueHelper.getSecondaryTextColor(
service, true
)
)
)
)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
}
private fun update(bitmap: Bitmap?, color: Int) {
// Set correct drawable for pause state
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, ImageUtil.createBitmap(
ImageUtil.getTintedVectorDrawable(
service, playPauseRes, color
)
)
)
// Set favorite button drawables
appWidgetView.setImageViewBitmap(
R.id.button_toggle_favorite, ImageUtil.createBitmap(
ImageUtil.getTintedVectorDrawable(
service, favoriteRes, color
)
)
)
appWidgetView.setImageViewBitmap(R.id.image, bitmap)
pushUpdate(service, appWidgetIds, appWidgetView)
}
})
}
}
/**
* Link up various button actions using [PendingIntent].
*/
private fun linkButtons(context: Context, views: RemoteViews) {
val action = Intent(context, MainActivity::class.java)
.putExtra(
MainActivity.EXPAND_PANEL,
PreferenceUtil.isExpandPanel
)
val serviceName = ComponentName(context, MusicService::class.java)
// Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
var pendingIntent =
PendingIntent.getActivity(
context, 0, action, if (VersionUtils.hasMarshmallow())
PendingIntent.FLAG_IMMUTABLE
else 0
)
views.setOnClickPendingIntent(R.id.image, pendingIntent)
// Favorite track
pendingIntent = buildPendingIntent(context, TOGGLE_FAVORITE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_favorite, pendingIntent)
// Play and pause
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
}
companion object {
const val NAME = "app_widget_circle"
private var mInstance: AppWidgetCircle? = null
private var imageSize = 0
val instance: AppWidgetCircle
@Synchronized get() {
if (mInstance == null) {
mInstance = AppWidgetCircle()
}
return mInstance!!
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,15 +5,14 @@ import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.Spanned import android.text.Spanned
import android.view.Menu import android.view.*
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.appcompat.widget.PopupMenu
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -32,6 +31,7 @@ import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.glide.SingleColorTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.interfaces.IAlbumClickListener import code.name.monkey.retromusic.interfaces.IAlbumClickListener
import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabCallback
import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.interfaces.ICabHolder
@ -39,10 +39,7 @@ import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.Result
import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.network.model.LastFmArtist
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.*
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialcab.attached.AttachedCab import com.afollestad.materialcab.attached.AttachedCab
import com.afollestad.materialcab.attached.destroy import com.afollestad.materialcab.attached.destroy
import com.afollestad.materialcab.attached.isActive import com.afollestad.materialcab.attached.isActive
@ -71,12 +68,15 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
private var lang: String? = null private var lang: String? = null
private var biography: Spanned? = null private var biography: Spanned? = null
private val savedSongSortOrder: String
get() = PreferenceUtil.artistDetailSongSortOrder
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform().apply { sharedElementEnterTransition = MaterialContainerTransform().apply {
drawingViewId = R.id.fragment_container drawingViewId = R.id.fragment_container
scrimColor = Color.TRANSPARENT scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface)) setAllContainerColors(surfaceColor())
} }
} }
@ -101,7 +101,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
setupRecyclerView() setupRecyclerView()
binding.fragmentArtistContent.playAction.apply { binding.fragmentArtistContent.playAction.apply {
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } setOnClickListener { MusicPlayerRemote.openQueue(artist.sortedSongs, 0, true) }
} }
binding.fragmentArtistContent.shuffleAction.apply { binding.fragmentArtistContent.shuffleAction.apply {
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) } setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
@ -121,6 +121,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
requireActivity().onBackPressed() requireActivity().onBackPressed()
} }
} }
setupSongSortButton()
binding.appBarLayout?.statusBarForeground = binding.appBarLayout?.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext()) MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
@ -168,7 +169,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
) )
binding.fragmentArtistContent.songTitle.text = songText binding.fragmentArtistContent.songTitle.text = songText
binding.fragmentArtistContent.albumTitle.text = albumText binding.fragmentArtistContent.albumTitle.text = albumText
songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber }) songAdapter.swapDataSet(artist.sortedSongs)
albumAdapter.swapDataSet(artist.albums) albumAdapter.swapDataSet(artist.albums)
} }
@ -192,19 +193,21 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
if (lastFmArtist != null && lastFmArtist.artist != null && lastFmArtist.artist.bio != null) { if (lastFmArtist != null && lastFmArtist.artist != null && lastFmArtist.artist.bio != null) {
val bioContent = lastFmArtist.artist.bio.content val bioContent = lastFmArtist.artist.bio.content
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) { if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
binding.fragmentArtistContent.biographyText.visibility = View.VISIBLE binding.fragmentArtistContent.run {
binding.fragmentArtistContent.biographyTitle.visibility = View.VISIBLE biographyText.isVisible = true
biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY) biographyTitle.isVisible = true
binding.fragmentArtistContent.biographyText.text = biography biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY)
if (lastFmArtist.artist.stats.listeners.isNotEmpty()) { biographyText.text = biography
binding.fragmentArtistContent.listeners.show() if (lastFmArtist.artist.stats.listeners.isNotEmpty()) {
binding.fragmentArtistContent.listenersLabel.show() listeners.show()
binding.fragmentArtistContent.scrobbles.show() listenersLabel.show()
binding.fragmentArtistContent.scrobblesLabel.show() scrobbles.show()
binding.fragmentArtistContent.listeners.text = scrobblesLabel.show()
RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat()) listeners.text =
binding.fragmentArtistContent.scrobbles.text = RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat())
RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat()) scrobbles.text =
RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat())
}
} }
} }
} }
@ -289,6 +292,53 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
return true return true
} }
private fun setupSongSortButton() {
binding.fragmentArtistContent.songSortOrder.setOnClickListener {
PopupMenu(requireContext(), binding.fragmentArtistContent.songSortOrder).apply {
inflate(R.menu.menu_artist_song_sort_order)
setUpSortOrderMenu(menu)
setOnMenuItemClickListener { menuItem ->
val sortOrder = when (menuItem.itemId) {
R.id.action_sort_order_title -> SortOrder.ArtistSongSortOrder.SONG_A_Z
R.id.action_sort_order_title_desc -> SortOrder.ArtistSongSortOrder.SONG_Z_A
R.id.action_sort_order_album -> SortOrder.ArtistSongSortOrder.SONG_ALBUM
R.id.action_sort_order_year -> SortOrder.ArtistSongSortOrder.SONG_YEAR
R.id.action_sort_order_song_duration -> SortOrder.ArtistSongSortOrder.SONG_DURATION
else -> {
throw IllegalArgumentException("invalid ${menuItem.title}")
}
}
menuItem.isChecked = true
setSaveSortOrder(sortOrder)
return@setOnMenuItemClickListener true
}
show()
}
}
}
private fun setSaveSortOrder(sortOrder: String) {
PreferenceUtil.artistDetailSongSortOrder = sortOrder
songAdapter.swapDataSet(artist.sortedSongs)
}
private fun setUpSortOrderMenu(sortOrder: Menu) {
when (savedSongSortOrder) {
SortOrder.ArtistSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title).isChecked = true
SortOrder.ArtistSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc).isChecked = true
SortOrder.ArtistSongSortOrder.SONG_ALBUM ->
sortOrder.findItem(R.id.action_sort_order_album).isChecked = true
SortOrder.ArtistSongSortOrder.SONG_YEAR ->
sortOrder.findItem(R.id.action_sort_order_year).isChecked = true
SortOrder.ArtistSongSortOrder.SONG_DURATION ->
sortOrder.findItem(R.id.action_sort_order_song_duration).isChecked = true
else-> {
throw IllegalArgumentException("invalid $savedSongSortOrder")
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when (requestCode) { when (requestCode) {

View file

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

View file

@ -17,6 +17,8 @@ import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.backup.BackupAdapter import code.name.monkey.retromusic.adapter.backup.BackupAdapter
import code.name.monkey.retromusic.databinding.FragmentBackupBinding import code.name.monkey.retromusic.databinding.FragmentBackupBinding
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.accentOutlineColor
import code.name.monkey.retromusic.helper.BackupHelper import code.name.monkey.retromusic.helper.BackupHelper
import code.name.monkey.retromusic.helper.sanitize import code.name.monkey.retromusic.helper.sanitize
import code.name.monkey.retromusic.util.BackupUtil import code.name.monkey.retromusic.util.BackupUtil
@ -45,7 +47,7 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
else else
backupAdapter?.swapDataset(listOf()) backupAdapter?.swapDataset(listOf())
} }
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) { val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
it?.let { it?.let {
@ -55,6 +57,8 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
} }
} }
} }
binding.createBackup.accentOutlineColor()
binding.restoreBackup.accentColor()
binding.createBackup.setOnClickListener { binding.createBackup.setOnClickListener {
showCreateBackupDialog() showCreateBackupDialog()
} }
@ -91,11 +95,11 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
MaterialDialog(requireContext()).show { MaterialDialog(requireContext()).show {
cornerRadius(res = R.dimen.m3_card_corner_radius) cornerRadius(res = R.dimen.m3_card_corner_radius)
title(res = R.string.action_rename) title(res = R.string.action_rename)
input(prefill = System.currentTimeMillis().toString()) { _, text -> input(prefill = BackupHelper.getTimeStamp()) { _, text ->
// Text submitted with the action button // Text submitted with the action button
lifecycleScope.launch { lifecycleScope.launch {
BackupHelper.createBackup(requireContext(), text.sanitize()) BackupHelper.createBackup(requireContext(), text.sanitize())
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
} }
} }
positiveButton(android.R.string.ok) positiveButton(android.R.string.ok)
@ -125,7 +129,7 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
return true return true
} }
R.id.action_share -> { R.id.action_share -> {
@ -143,10 +147,10 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
input(prefill = file.nameWithoutExtension) { _, text -> input(prefill = file.nameWithoutExtension) { _, text ->
// Text submitted with the action button // Text submitted with the action button
val renamedFile = val renamedFile =
File(file.parent + File.separator + text + BackupHelper.APPEND_EXTENSION) File(file.parent, "$text${BackupHelper.APPEND_EXTENSION}")
if (!renamedFile.exists()) { if (!renamedFile.exists()) {
file.renameTo(renamedFile) file.renameTo(renamedFile)
backupViewModel.loadBackups() backupViewModel.loadBackups(requireContext())
} else { } else {
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),

View file

@ -1,6 +1,7 @@
package code.name.monkey.retromusic.fragments.backup package code.name.monkey.retromusic.fragments.backup
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -19,8 +20,8 @@ class BackupViewModel : ViewModel() {
private val backupsMutableLiveData = MutableLiveData<List<File>>() private val backupsMutableLiveData = MutableLiveData<List<File>>()
val backupsLiveData: LiveData<List<File>> = backupsMutableLiveData val backupsLiveData: LiveData<List<File>> = backupsMutableLiveData
fun loadBackups() { fun loadBackups(context: Context) {
File(BackupHelper.backupRootPath).listFiles { _, name -> BackupHelper.getBackupRoot().listFiles { _, name ->
return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION) return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION)
}?.toList()?.let { }?.toList()?.let {
backupsMutableLiveData.value = it backupsMutableLiveData.value = it
@ -29,13 +30,16 @@ class BackupViewModel : ViewModel() {
suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List<BackupContent>) { suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List<BackupContent>) {
BackupHelper.restoreBackup(activity, inputStream, contents) BackupHelper.restoreBackup(activity, inputStream, contents)
withContext(Dispatchers.Main) { if (contents.contains(BackupContent.SETTINGS) or contents.contains(BackupContent.CUSTOM_ARTIST_IMAGES)) {
val intent = Intent( // We have to restart App when Preferences i.e. Settings or Artist Images are to be restored
activity, withContext(Dispatchers.Main) {
MainActivity::class.java val intent = Intent(
) activity,
activity.startActivity(intent) MainActivity::class.java
exitProcess(0) )
activity.startActivity(intent)
exitProcess(0)
}
} }
} }
} }

View file

@ -1,11 +1,14 @@
package code.name.monkey.retromusic.fragments.backup package code.name.monkey.retromusic.fragments.backup
import android.content.ContentResolver
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.view.ViewGroup
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.databinding.ActivityRestoreBinding import code.name.monkey.retromusic.databinding.ActivityRestoreBinding
import code.name.monkey.retromusic.helper.BackupContent import code.name.monkey.retromusic.helper.BackupContent
@ -27,6 +30,7 @@ class RestoreActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityRestoreBinding.inflate(layoutInflater) binding = ActivityRestoreBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setWidth()
val backupUri = intent?.data val backupUri = intent?.data
binding.backupName.setText(getFileName(backupUri)) binding.backupName.setText(getFileName(backupUri))
binding.cancelButton.setOnClickListener { binding.cancelButton.setOnClickListener {
@ -35,7 +39,6 @@ class RestoreActivity : AppCompatActivity() {
binding.restoreButton.setOnClickListener { binding.restoreButton.setOnClickListener {
val backupContents = mutableListOf<BackupContent>() val backupContents = mutableListOf<BackupContent>()
if (binding.checkSettings.isChecked) backupContents.add(SETTINGS) if (binding.checkSettings.isChecked) backupContents.add(SETTINGS)
if (binding.checkQueue.isChecked) backupContents.add(QUEUE)
if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS) if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS)
if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES) if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES)
if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES) if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES)
@ -50,7 +53,7 @@ class RestoreActivity : AppCompatActivity() {
} }
private fun updateTheme() { private fun updateTheme() {
AppCompatDelegate.setDefaultNightMode(ThemeManager.getNightMode(this)) AppCompatDelegate.setDefaultNightMode(ThemeManager.getNightMode())
// Apply dynamic colors to activity if enabled // Apply dynamic colors to activity if enabled
if (PreferenceUtil.materialYou) { if (PreferenceUtil.materialYou) {
@ -63,21 +66,17 @@ class RestoreActivity : AppCompatActivity() {
private fun getFileName(uri: Uri?): String? { private fun getFileName(uri: Uri?): String? {
when (uri?.scheme) { when (uri?.scheme) {
"file" -> { ContentResolver.SCHEME_FILE -> {
return uri.lastPathSegment return uri.lastPathSegment
} }
"content" -> { ContentResolver.SCHEME_CONTENT -> {
val proj = arrayOf(MediaStore.Images.Media.TITLE) val proj = arrayOf(MediaStore.Files.FileColumns.DISPLAY_NAME)
contentResolver.query( contentResolver.query(
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { uri, proj, null, null, null
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}, proj, null, null, null
)?.use { cursor -> )?.use { cursor ->
if (cursor.count != 0) { if (cursor.count != 0) {
val columnIndex: Int = val columnIndex: Int =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE) cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
cursor.moveToFirst() cursor.moveToFirst()
return cursor.getString(columnIndex) return cursor.getString(columnIndex)
} }
@ -86,4 +85,9 @@ class RestoreActivity : AppCompatActivity() {
} }
return "Backup" return "Backup"
} }
private fun setWidth() {
val width = resources.displayMetrics.widthPixels * 0.8
binding.root.updateLayoutParams<ViewGroup.LayoutParams> { this.width = width.toInt() }
}
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -48,7 +48,7 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
}) })
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
remove() remove()
mainActivity.finish() requireActivity().onBackPressed()
} }
} }
@ -62,7 +62,7 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
override fun createAdapter(): GenreAdapter { override fun createAdapter(): GenreAdapter {
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
return GenreAdapter(requireActivity(), dataSet, R.layout.item_genre, this) return GenreAdapter(requireActivity(), dataSet, this)
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -29,6 +29,7 @@ import android.widget.Toast
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_BANNER
import code.name.monkey.retromusic.Constants.USER_PROFILE import code.name.monkey.retromusic.Constants.USER_PROFILE
@ -50,7 +51,6 @@ import com.bumptech.glide.request.target.Target
import com.github.dhaval2404.imagepicker.ImagePicker import com.github.dhaval2404.imagepicker.ImagePicker
import com.github.dhaval2404.imagepicker.constant.ImageProvider import com.github.dhaval2404.imagepicker.constant.ImageProvider
import com.google.android.material.transition.MaterialContainerTransform import com.google.android.material.transition.MaterialContainerTransform
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -202,15 +202,15 @@ class UserInfoFragment : Fragment() {
} }
private fun saveImage(bitmap: Bitmap, fileName: String) { private fun saveImage(bitmap: Bitmap, fileName: String) {
CoroutineScope(Dispatchers.IO).launch { lifecycleScope.launch(Dispatchers.IO) {
val appDir = requireContext().filesDir val appDir = requireContext().filesDir
val file = File(appDir, fileName) val file = File(appDir, fileName)
var successful = false var successful = false
runCatching { runCatching {
val os = BufferedOutputStream(FileOutputStream(file)) BufferedOutputStream(FileOutputStream(file)).use {
successful = ImageUtil.resizeBitmap(bitmap, 2048) successful = ImageUtil.resizeBitmap(bitmap, 2048)
.compress(Bitmap.CompressFormat.WEBP, 100, os) .compress(Bitmap.CompressFormat.WEBP, 100, it)
withContext(Dispatchers.IO) { os.close() } }
}.onFailure { }.onFailure {
it.printStackTrace() it.printStackTrace()
} }

View file

@ -14,17 +14,19 @@
*/ */
package code.name.monkey.retromusic.fragments.player package code.name.monkey.retromusic.fragments.player
import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.view.isInvisible import androidx.core.animation.doOnEnd
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.LYRICS_TYPE
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.SHOW_LYRICS import code.name.monkey.retromusic.SHOW_LYRICS
import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter
@ -34,7 +36,6 @@ import code.name.monkey.retromusic.extensions.isColorLight
import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToLyrics import code.name.monkey.retromusic.fragments.base.goToLyrics
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
@ -43,6 +44,7 @@ import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.transform.CarousalPagerTransformer import code.name.monkey.retromusic.transform.CarousalPagerTransformer
import code.name.monkey.retromusic.transform.ParallaxPagerTransformer import code.name.monkey.retromusic.transform.ParallaxPagerTransformer
import code.name.monkey.retromusic.util.LyricUtil import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.LyricsType
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -145,21 +147,6 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
setOnClickListener { setOnClickListener {
goToLyrics(requireActivity()) goToLyrics(requireActivity())
} }
setOnFlingXListener { velocityX ->
when {
velocityX < 0 -> {
MusicPlayerRemote.playNextSong()
true
}
velocityX > 0 -> {
MusicPlayerRemote.playPreviousSong()
true
}
else -> {
false
}
}
}
} }
} }
@ -201,6 +188,8 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
showLyrics(false) showLyrics(false)
progressViewUpdateHelper?.stop() progressViewUpdateHelper?.stop()
} }
} else if (key == LYRICS_TYPE) {
maybeInitLyrics()
} }
} }
@ -223,8 +212,22 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} }
private fun showLyrics(visible: Boolean) { private fun showLyrics(visible: Boolean) {
lrcView.isVisible = visible binding.coverLyrics.isVisible = false
viewPager.isInvisible = visible binding.lyricsView.isVisible = false
binding.viewPager.isVisible = true
val lyrics: View = if (PreferenceUtil.lyricsType == LyricsType.REPLACE_COVER) {
ObjectAnimator.ofFloat(viewPager, View.ALPHA, if (visible) 0F else 1F).start()
lrcView
} else {
ObjectAnimator.ofFloat(viewPager, View.ALPHA, 1F).start()
binding.coverLyrics
}
ObjectAnimator.ofFloat(lyrics, View.ALPHA, if (visible) 1F else 0F).apply {
doOnEnd {
lyrics.isVisible = visible
}
start()
}
} }
private fun maybeInitLyrics() { private fun maybeInitLyrics() {
@ -232,9 +235,9 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
// Don't show lyrics container for below conditions // Don't show lyrics container for below conditions
if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) { if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) {
showLyrics(true) showLyrics(true)
progressViewUpdateHelper?.start() if (PreferenceUtil.lyricsType == LyricsType.REPLACE_COVER) {
lrcView.animate().alpha(1f).duration = progressViewUpdateHelper?.start()
AbsPlayerFragment.VISIBILITY_ANIM_DURATION }
} else { } else {
showLyrics(false) showLyrics(false)
progressViewUpdateHelper?.stop() progressViewUpdateHelper?.stop()
@ -280,9 +283,9 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} else { } else {
surfaceColor() surfaceColor()
} }
Color -> color.backgroundColor Color ,Classic -> color.backgroundColor
Blur -> Color.BLACK Blur -> Color.BLACK
else -> color.backgroundColor else -> surfaceColor()
} }
) )
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -60,11 +60,13 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
_binding?.root?.setBackgroundColor(color.backgroundColor) _binding?.root?.setBackgroundColor(color.backgroundColor)
} }
animator.start() animator.start()
ToolbarContentTintHelper.colorizeToolbar( binding.playerToolbar.post {
binding.playerToolbar, ToolbarContentTintHelper.colorizeToolbar(
color.secondaryTextColor, binding.playerToolbar,
requireActivity() color.secondaryTextColor,
) requireActivity()
)
}
} }
override fun onFavoriteToggled() { override fun onFavoriteToggled() {

View file

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

View file

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

View file

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

View file

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

View file

@ -35,11 +35,13 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding
import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.MusicSeekSkipTouchListener
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
@ -89,8 +91,8 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
binding.playerQueueSheet.updatePadding( binding.playerQueueSheet.updatePadding(
top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt() top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt()
) )
binding.recyclerView.updatePadding( binding.container.updatePadding(
top = ((1 - slideOffset) * navBarHeight).toInt() bottom = ((1 - slideOffset) * navBarHeight).toInt()
) )
} }
@ -158,9 +160,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
} }
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
(binding.container) (binding.container)
) { _: View, insets: WindowInsetsCompat -> ) { v: View, insets: WindowInsetsCompat ->
navBarHeight = insets.safeGetBottomInsets() navBarHeight = insets.safeGetBottomInsets()
binding.recyclerView.updatePadding(top = navBarHeight) v.updatePadding(bottom = navBarHeight)
insets insets
} }
binding.playbackControlsFragment.root.drawAboveSystemBars() binding.playbackControlsFragment.root.drawAboveSystemBars()
@ -282,7 +284,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
val isFavorite: Boolean = val isFavorite: Boolean =
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id) libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val icon = if (animate) { val icon = if (animate && VersionUtils.hasMarshmallow()) {
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
} else { } else {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
@ -385,10 +387,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
) )
} }
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() { private fun setUpPrevNext() {
updatePrevNextColor() updatePrevNextColor()
binding.playbackControlsFragment.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() } binding.playbackControlsFragment.nextButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), true))
binding.playbackControlsFragment.previousButton.setOnClickListener { MusicPlayerRemote.back() } binding.playbackControlsFragment.previousButton.setOnTouchListener(MusicSeekSkipTouchListener(requireActivity(), false))
} }
private fun updatePrevNextColor() { private fun updatePrevNextColor() {
@ -453,7 +456,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private fun updateLabel() { private fun updateLabel() {
(MusicPlayerRemote.playingQueue.size - 1).apply { (MusicPlayerRemote.playingQueue.size - 1).apply {
if (this == (MusicPlayerRemote.position)) { if (this == (MusicPlayerRemote.position)) {
binding.nextSong.text = "Last song" binding.nextSong.text = context?.resources?.getString(R.string.last_song)
} else { } else {
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
binding.nextSong.text = title binding.nextSong.text = title
@ -473,7 +476,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
oldBottom: Int oldBottom: Int
) { ) {
val panel = getQueuePanel() val panel = getQueuePanel()
panel.peekHeight = binding.container.height + navBarHeight if (panel.state == STATE_COLLAPSED) {
panel.peekHeight = binding.container.height
} else if (panel.state == STATE_EXPANDED) {
panel.peekHeight = binding.container.height + navBarHeight
}
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,39 +2,49 @@ package code.name.monkey.retromusic.helper
import android.content.Context import android.content.Context
import android.os.Environment import android.os.Environment
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.BuildConfig import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.helper.BackupContent.* import code.name.monkey.retromusic.helper.BackupContent.*
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.Repository
import code.name.monkey.retromusic.repository.SongRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.* import java.io.*
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
object BackupHelper { object BackupHelper : KoinComponent {
private val repository by inject<Repository>()
private val songRepository by inject<SongRepository>()
suspend fun createBackup(context: Context, name: String) { suspend fun createBackup(context: Context, name: String) {
val backupFile = val backupFile =
File(backupRootPath + File.separator + name + APPEND_EXTENSION) File(getBackupRoot(), name + APPEND_EXTENSION)
if (backupFile.parentFile?.exists() != true) { if (backupFile.parentFile?.exists() != true) {
backupFile.parentFile?.mkdirs() backupFile.parentFile?.mkdirs()
} }
val zipItems = mutableListOf<ZipItem>() val zipItems = mutableListOf<ZipItem>()
zipItems.addAll(getDatabaseZipItems(context)) zipItems.addAll(getPlaylistZipItems(context))
zipItems.addAll(getSettingsZipItems(context)) zipItems.addAll(getSettingsZipItems(context))
getUserImageZipItems(context)?.let { zipItems.addAll(it) } getUserImageZipItems(context)?.let { zipItems.addAll(it) }
zipItems.addAll(getCustomArtistZipItems(context)) zipItems.addAll(getCustomArtistZipItems(context))
zipItems.addAll(getQueueZipItems(context))
zipAll(zipItems, backupFile) zipAll(zipItems, backupFile)
// Clean Cache Playlist Directory
File(context.filesDir, PLAYLISTS_PATH).deleteRecursively()
} }
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) = private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
kotlin.runCatching { runCatching {
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out -> ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
for (zipItem in zipItems) { for (zipItem in zipItems) {
FileInputStream(zipItem.filePath).use { fi -> FileInputStream(zipItem.filePath).use { fi ->
@ -51,7 +61,6 @@ object BackupHelper {
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT) Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
.show() .show()
} }
throw Exception(it)
}.onSuccess { }.onSuccess {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
Toast.makeText( Toast.makeText(
@ -62,34 +71,39 @@ object BackupHelper {
.show() .show()
} }
} }
} }
private fun getDatabaseZipItems(context: Context): List<ZipItem> { private suspend fun getPlaylistZipItems(context: Context): List<ZipItem> {
return context.databaseList().filter { val playlistZipItems = mutableListOf<ZipItem>()
it.endsWith(".db") && it != queueDatabase // Cache Playlist files in App storage
}.map { val playlistFolder = File(context.filesDir, PLAYLISTS_PATH)
ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it") if (!playlistFolder.exists()) {
playlistFolder.mkdirs()
} }
} for (playlist in repository.fetchPlaylistWithSongs()) {
runCatching {
private fun getQueueZipItems(context: Context): List<ZipItem> { M3UWriter.writeIO(playlistFolder, playlist)
Log.d("RetroMusic", context.getDatabasePath(queueDatabase).absolutePath) }.onSuccess { playlistFile ->
return listOf( if (playlistFile.exists()) {
ZipItem( playlistZipItems.add(
context.getDatabasePath(queueDatabase).absolutePath, ZipItem(
"$QUEUE_PATH${File.separator}$queueDatabase" playlistFile.absolutePath,
) PLAYLISTS_PATH.child(playlistFile.name)
) )
)
}
}
}
return playlistZipItems
} }
private fun getSettingsZipItems(context: Context): List<ZipItem> { private fun getSettingsZipItems(context: Context): List<ZipItem> {
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" val sharedPrefPath = File(context.filesDir.parentFile, "shared_prefs")
return listOf( return listOf(
"${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path "${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path
"$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path "$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path
).map { ).map {
ZipItem(sharedPrefPath + it, "$SETTINGS_PATH${File.separator}$it") ZipItem(File(sharedPrefPath, it).absolutePath, SETTINGS_PATH.child(it))
} }
} }
@ -97,35 +111,33 @@ object BackupHelper {
return context.filesDir.listFiles { _, name -> return context.filesDir.listFiles { _, name ->
name.endsWith(".jpg") name.endsWith(".jpg")
}?.map { }?.map {
ZipItem(it.absolutePath, "$IMAGES_PATH${File.separator}${it.name}") ZipItem(it.absolutePath, IMAGES_PATH.child(it.name))
} }
} }
private fun getCustomArtistZipItems(context: Context): List<ZipItem> { private fun getCustomArtistZipItems(context: Context): List<ZipItem> {
val zipItemList = mutableListOf<ZipItem>() val zipItemList = mutableListOf<ZipItem>()
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" val sharedPrefPath = File(context.filesDir.parentFile, "shared_prefs")
zipItemList.addAll( zipItemList.addAll(
File(context.filesDir, "custom_artist_images") File(context.filesDir, "custom_artist_images")
.listFiles()?.map { .listFiles()?.map {
ZipItem( ZipItem(
it.absolutePath, it.absolutePath,
"$CUSTOM_ARTISTS_PATH${File.separator}custom_artist_images${File.separator}${it.name}" CUSTOM_ARTISTS_PATH.child("custom_artist_images").child(it.name)
) )
}?.toList() ?: listOf() }?.toList() ?: listOf()
) )
File(sharedPrefPath + File.separator + "custom_artist_image.xml").let { File(sharedPrefPath, "custom_artist_image.xml").let {
if (it.exists()) { if (it.exists()) {
zipItemList.add( zipItemList.add(
ZipItem( ZipItem(
it.absolutePath, it.absolutePath,
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml" CUSTOM_ARTISTS_PATH.child("prefs").child("custom_artist_image.xml")
) )
) )
} }
} }
return zipItemList return zipItemList
} }
@ -138,23 +150,19 @@ object BackupHelper {
ZipInputStream(inputStream).use { ZipInputStream(inputStream).use {
var entry = it.nextEntry var entry = it.nextEntry
while (entry != null) { while (entry != null) {
if (entry.isDatabaseEntry() && contents.contains(PLAYLISTS)) { if (entry.isPlaylistEntry() && contents.contains(PLAYLISTS)) {
restoreDatabase(context, it, entry) restorePlaylists(it, entry)
} else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) { } else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) {
restorePreferences(context, it, entry) restorePreferences(context, it, entry)
} else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) { } else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) {
restoreImages(context, it, entry) restoreImages(context, it, entry)
} else if (entry.isCustomArtistEntry() && contents.contains(CUSTOM_ARTIST_IMAGES)) {
} else if (entry.isCustomArtistImageEntry() && contents.contains( if (entry.isCustomArtistPrefEntry()) {
CUSTOM_ARTIST_IMAGES restoreCustomArtistPrefs(context, it, entry)
) } else if (entry.isCustomArtistImageEntry()) {
) { restoreCustomArtistImages(context, it, entry)
restoreCustomArtistImages(context, it, entry) }
restoreCustomArtistPrefs(context, it, entry)
} else if (entry.isQueueEntry() && contents.contains(QUEUE)) {
restoreQueueDatabase(context, it, entry)
} }
entry = it.nextEntry entry = it.nextEntry
} }
} }
@ -165,14 +173,11 @@ object BackupHelper {
} }
private fun restoreImages(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { private fun restoreImages(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
val filePath = val file = File(
context.filesDir.path + File.separator + zipEntry.getFileName() context.filesDir.path, zipEntry.getFileName()
BufferedOutputStream(FileOutputStream(filePath)).use { bos -> )
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) BufferedOutputStream(FileOutputStream(file)).use { bos ->
var read: Int zipIn.copyTo(bos)
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
@ -184,39 +189,38 @@ object BackupHelper {
file.delete() file.delete()
} }
BufferedOutputStream(FileOutputStream(file)).use { bos -> BufferedOutputStream(FileOutputStream(file)).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) zipIn.copyTo(bos)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
private fun restoreDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { private suspend fun restorePlaylists(
val filePath = zipIn: ZipInputStream,
context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName() zipEntry: ZipEntry
BufferedOutputStream(FileOutputStream(filePath)).use { bos -> ) {
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) val playlistName = zipEntry.getFileName().substringBeforeLast(".")
var read: Int val songs = mutableListOf<Song>()
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
private fun restoreQueueDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { // Get songs from m3u playlist files
PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) { zipIn.bufferedReader().lineSequence().forEach { line ->
putInt("POSITION", 0) if (line.startsWith(File.separator)) {
} if (File(line).exists()) {
val filePath = songs.addAll(songRepository.songsByFilePath(line))
context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName() }
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
} }
} }
val playlistEntity = repository.checkPlaylistExists(playlistName).firstOrNull()
if (playlistEntity != null) {
val songEntities = songs.map {
it.toSongEntity(playlistEntity.playListId)
}
repository.insertSongs(songEntities)
} else {
val playListId = repository.createPlaylist(PlaylistEntity(playlistName = playlistName))
val songEntities = songs.map {
it.toSongEntity(playListId)
}
repository.insertSongs(songEntities)
}
} }
private fun restoreCustomArtistImages( private fun restoreCustomArtistImages(
@ -237,11 +241,7 @@ object BackupHelper {
) )
) )
).use { bos -> ).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) zipIn.copyTo(bos)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
@ -250,32 +250,30 @@ object BackupHelper {
zipIn: ZipInputStream, zipIn: ZipInputStream,
zipEntry: ZipEntry zipEntry: ZipEntry
) { ) {
val filePath = val file =
context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + zipEntry.getFileName() File(context.filesDir.parentFile, "shared_prefs".child(zipEntry.getFileName()))
BufferedOutputStream(FileOutputStream(filePath)).use { bos -> BufferedOutputStream(FileOutputStream(file)).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) zipIn.copyTo(bos)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
} }
} }
val backupRootPath = fun getBackupRoot(): File {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) return File(
.toString() + "/RetroMusic/Backups/" Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
"RetroMusic/Backups"
)
}
const val BACKUP_EXTENSION = "rmbak" const val BACKUP_EXTENSION = "rmbak"
const val APPEND_EXTENSION = ".$BACKUP_EXTENSION" const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
private const val DATABASES_PATH = "databases" private const val PLAYLISTS_PATH = "Playlists"
private const val QUEUE_PATH = "queue"
private const val SETTINGS_PATH = "prefs" private const val SETTINGS_PATH = "prefs"
private const val IMAGES_PATH = "userImages" private const val IMAGES_PATH = "userImages"
private const val CUSTOM_ARTISTS_PATH = "artistImages" private const val CUSTOM_ARTISTS_PATH = "artistImages"
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]" private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
private const val queueDatabase = "music_playback_state.db"
private fun ZipEntry.isDatabaseEntry(): Boolean { private fun ZipEntry.isPlaylistEntry(): Boolean {
return name.startsWith(DATABASES_PATH) return name.startsWith(PLAYLISTS_PATH)
} }
private fun ZipEntry.isPreferenceEntry(): Boolean { private fun ZipEntry.isPreferenceEntry(): Boolean {
@ -286,6 +284,10 @@ object BackupHelper {
return name.startsWith(IMAGES_PATH) return name.startsWith(IMAGES_PATH)
} }
private fun ZipEntry.isCustomArtistEntry(): Boolean {
return name.startsWith(CUSTOM_ARTISTS_PATH)
}
private fun ZipEntry.isCustomArtistImageEntry(): Boolean { private fun ZipEntry.isCustomArtistImageEntry(): Boolean {
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("custom_artist_images") return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("custom_artist_images")
} }
@ -294,12 +296,12 @@ object BackupHelper {
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs") return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
} }
private fun ZipEntry.isQueueEntry(): Boolean { private fun ZipEntry.getFileName(): String {
return name.startsWith(QUEUE_PATH) return name.substring(name.lastIndexOf(File.separator) + 1)
} }
private fun ZipEntry.getFileName(): String { fun getTimeStamp(): String {
return name.substring(name.lastIndexOf(File.separator)) return SimpleDateFormat("dd-MMM yyyy HHmmss", Locale.getDefault()).format(Date())
} }
} }
@ -318,10 +320,13 @@ fun CharSequence.sanitize(): String {
.replace("&", "_") .replace("&", "_")
} }
fun String.child(child: String): String {
return this + File.separator + child
}
enum class BackupContent { enum class BackupContent {
SETTINGS, SETTINGS,
USER_IMAGES, USER_IMAGES,
CUSTOM_ARTIST_IMAGES, CUSTOM_ARTIST_IMAGES,
PLAYLISTS, PLAYLISTS
QUEUE
} }

View file

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

View file

@ -134,7 +134,7 @@ class SortOrder {
companion object { companion object {
/* Artist song sort order A-Z */ /* Artist song sort order A-Z */
private const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER
/* Artist song sort order Z-A */ /* Artist song sort order Z-A */
const val SONG_Z_A = "$SONG_A_Z DESC" const val SONG_Z_A = "$SONG_A_Z DESC"

View file

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

View file

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

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