Merge pull request #1191 from prathameshmm02/dev
Added New features and bug fixes
This commit is contained in:
commit
c988f2f0fd
183 changed files with 3591 additions and 1574 deletions
|
@ -5,18 +5,17 @@ apply plugin: "androidx.navigation.safeargs.kotlin"
|
|||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdk 31
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
minSdk 21
|
||||
targetSdk 31
|
||||
|
||||
renderscriptTargetApi 29//must match target sdk and build tools
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
applicationId "code.name.monkey.retromusic"
|
||||
versionCode 10545
|
||||
versionName '5.4.2 ' + "_" + getDate()
|
||||
versionCode 10551
|
||||
versionName '5.5.0'
|
||||
|
||||
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
||||
}
|
||||
|
@ -31,9 +30,10 @@ android {
|
|||
}
|
||||
buildTypes {
|
||||
release {
|
||||
//debuggable true
|
||||
versionNameSuffix "_" + getDate()
|
||||
shrinkResources true
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
debug {
|
||||
|
@ -95,22 +95,21 @@ dependencies {
|
|||
implementation 'androidx.annotation:annotation:1.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0-beta01'
|
||||
implementation "androidx.preference:preference-ktx:$preference_version"
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.palette:palette-ktx:1.0.0'
|
||||
|
||||
//Cast Dependencies
|
||||
implementation 'androidx.mediarouter:mediarouter:1.2.5'
|
||||
implementation 'com.google.android.gms:play-services-cast-framework:20.1.0'
|
||||
implementation 'com.google.android.gms:play-services-cast-framework:21.0.0'
|
||||
//WebServer by NanoHttpd
|
||||
implementation "org.nanohttpd:nanohttpd:2.3.1"
|
||||
|
||||
def nav_version = '2.4.0-beta02'
|
||||
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-runtime-ktx:$navigation_version"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
|
||||
|
||||
def room_version = '2.4.0-rc01'
|
||||
def room_version = '2.4.0'
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
@ -121,7 +120,7 @@ dependencies {
|
|||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
|
||||
implementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
implementation 'com.google.android.material:material:1.5.0-beta01'
|
||||
implementation "com.google.android.material:material:$mdc_version"
|
||||
|
||||
def retrofit_version = '2.9.0'
|
||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||
|
@ -138,7 +137,7 @@ dependencies {
|
|||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
|
||||
def kotlin_coroutines_version = '1.6.0-RC'
|
||||
def kotlin_coroutines_version = '1.6.0-RC3'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
|
||||
|
@ -152,15 +151,19 @@ dependencies {
|
|||
|
||||
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
|
||||
|
||||
implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0'
|
||||
|
||||
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC3'
|
||||
implementation 'com.github.jetradarmobile:android-snowfall:1.2.1'
|
||||
|
||||
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
|
||||
implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3'
|
||||
implementation 'com.github.Adonai:jaudiotagger:2.3.15'
|
||||
implementation 'com.anjlab.android.iab.v3:library:2.0.3'
|
||||
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
||||
implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
|
||||
implementation 'com.github.dhaval2404:imagepicker:2.1'
|
||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
|
||||
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
||||
implementation 'me.tankery.lib:circularSeekBar:1.3.2'
|
||||
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'
|
||||
}
|
||||
|
||||
apply from: '../spotless.gradle'
|
||||
}
|
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
|
@ -49,8 +49,11 @@
|
|||
#-dontwarn
|
||||
#-ignorewarnings
|
||||
|
||||
#Jaudiotagger
|
||||
-dontwarn org.jaudiotagger.**
|
||||
-dontwarn org.jcodec.**
|
||||
-keep class org.jaudiotagger.** { *; }
|
||||
-keep class org.jcodec.** { *; }
|
||||
|
||||
-keepclassmembers enum * { *; }
|
||||
-keepattributes *Annotation*, Signature, Exception
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="md3_available">true</bool>
|
||||
<bool name="allowBackup">false</bool>
|
||||
</resources>
|
|
@ -94,8 +94,7 @@
|
|||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="circleImageView" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<style name="circleImageView" parent="ShapeAppearance.MaterialComponents">
|
||||
<item name="cornerSize">40dp</item>
|
||||
</style>
|
||||
|
||||
|
|
|
@ -22,11 +22,12 @@
|
|||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="@bool/allowBackup"
|
||||
android:configChanges="locale|layoutDirection"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:restoreAnyVersion="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.RetroMusic.FollowSystem"
|
||||
|
@ -122,15 +123,38 @@
|
|||
<activity android:name=".activities.LockScreenActivity" />
|
||||
<activity
|
||||
android:name=".fragments.backup.RestoreActivity"
|
||||
android:exported="true">
|
||||
android:excludeFromRecents="false"
|
||||
android:exported="true"
|
||||
android:label="@string/restore"
|
||||
android:theme="@style/Theme.RetroMusic.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
<data android:mimeType="application/x-zip-compressed" />
|
||||
<data android:mimeType="application/zip" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="*/*" />
|
||||
<!--
|
||||
Work around Android's ugly primitive PatternMatcher
|
||||
implementation that can't cope with finding a . early in
|
||||
the path unless it's explicitly matched.
|
||||
-->
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
@ -272,10 +296,9 @@
|
|||
<service
|
||||
android:name=".service.MusicService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:label="@string/app_name"
|
||||
tools:ignore="ExportedService">
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
|
@ -298,4 +321,12 @@
|
|||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||
android:value="code.name.monkey.retromusic.cast.CastOptionsProvider" />
|
||||
</application>
|
||||
|
||||
<!--
|
||||
This is not that important, it's just here so that we can query equalizer package
|
||||
and check if it's present on A11+ because of Package visibility restrictions.
|
||||
-->
|
||||
<queries>
|
||||
<package android:name="com.android.musicfx" />
|
||||
</queries>
|
||||
</manifest>
|
||||
|
|
|
@ -24,40 +24,54 @@
|
|||
padding-top: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p><b><a href="https://github.com/kabouzeid/Phonograph" title="Phonograph"> Phonograph</a></b> by
|
||||
Karim Abou Zeid</p>
|
||||
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
|
||||
by Aidan Michael Follestad</p>
|
||||
<p><b><a href="http://developer.android.com/tools/support-library/index.html"
|
||||
title="AOSP Support Libraries"> AOSP Support Libraries</a></b>by AOSP contributors</p>
|
||||
title="AOSP Support Libraries">AOSP Support Libraries</a></b> by AOSP contributors</p>
|
||||
<p><b><a href="https://github.com/bumptech/glide" title="Glide"> Glide</a></b> by Sam Judd</p>
|
||||
<p><b><a href="https://github.com/square/retrofit" title="Retrofit"> Retrofit</a></b> by Square team
|
||||
</p>
|
||||
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
|
||||
<p><b><a href="https://github.com/InsertKoinIO/koin"
|
||||
title="Koin">Koin</a></b> by Arnaud Giuliani</p>
|
||||
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
|
||||
by Aidan Michael Follestad</p>
|
||||
<p><b><a href="https://github.com/afollestad/material-cab" title="Material Contextual Action Bar">
|
||||
Material Contextual Action Bar</a></b> by Aidan Michael Follestad</p>
|
||||
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
|
||||
<p><b><a href="https://github.com/hdodenhof/CircleImageView" title="CircleImageView">
|
||||
CircleImageView</a></b> by Henning Dodenhof</p>
|
||||
<p><b><a href="https://github.com/DreaminginCodeZH/MaterialProgressBar" title="MaterialProgressBar">
|
||||
MaterialProgressBar</a></b> by Zhang Hai</p>
|
||||
<p><b><a href="https://github.com/anjlab/android-inapp-billing-v3"
|
||||
title="Android In-App Billing v3 Library"> Android In-App Billing v3 Library</a></b> by
|
||||
Henning Dodenhof</p>
|
||||
<p><b><a href="https://github.com/h6ah4i/android-advancedrecyclerview"
|
||||
title="Advanced RecyclerView"> Advanced RecyclerView</a></b> by Haruki Hasegawa</p>
|
||||
<p><b><a href="https://github.com/ksoichiro/Android-ObservableScrollView"
|
||||
title="Android-ObservableScrollView"> Android-ObservableScrollView</a></b> by Soichiro
|
||||
Kashima</p>
|
||||
<p><b><a href="https://github.com/Ereza/CustomActivityOnCrash"
|
||||
title="Custom Activity on Crash">Custom Activity on Crash</a></b> by Eduard Ereza Martínez
|
||||
</p>
|
||||
<p><b><a href="https://github.com/NanoHttpd/nanohttpd"
|
||||
title="NanoHttpd">NanoHttpd</a></b> by NanoHttpd Team</p>
|
||||
<p><b><a href="https://github.com/tankery/CircularSeekBar"
|
||||
title="Circular Seekbar">Circular Seekbar</a></b> by Tankery</p>
|
||||
<p><b><a href="https://github.com/Kaned1as/jaudiotagger"
|
||||
title="jAudioTagger">jAudioTagger</a></b> by Kanedias</p>
|
||||
<p><b><a href="https://github.com/zhanghai/AndroidFastScroll"
|
||||
title="Android Fast Scroll">Android Fast Scroll</a></b> by Zhang Hai</p>
|
||||
<p><b><a href="https://github.com/Dhaval2404/ImagePicker"
|
||||
title="Image Picker">Image Picker</a></b> by Dhaval Patel</p>
|
||||
<p><b><a href="https://github.com/heinrichreimer/material-intro"
|
||||
title="Material Intro">Material Intro</a></b> by Jan Heinrich Reimer</p>
|
||||
<p><b><a href="https://github.com/r0adkll/Slidr"
|
||||
title="Slidr">Slidr</a></b> by Drew Heavner</p>
|
||||
<p><b><a href="https://github.com/bosphere/Android-FadingEdgeLayout"
|
||||
title="FadingEdgeLayout">FadingEdgeLayout</a></b> by bosphere</p>
|
||||
<p><b><a href="https://github.com/yshrsmz/KeyboardVisibilityEvent"
|
||||
title="KeyboardVisibilityEvent">KeyboardVisibilityEvent</a></b> by Yasuhiro SHIMIZU</p>
|
||||
<p><b><a href="https://github.com/JetradarMobile/android-snowfall"
|
||||
title="android-snowfall">android-snowfall</a></b> by Jetradar Mobile</p>
|
||||
<p><b><a href="https://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>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -63,7 +63,75 @@
|
|||
|
||||
<body>
|
||||
<div>
|
||||
<h5>Date</h5>
|
||||
<h5>December 24, 2021</h5>
|
||||
<h2>v5.5.0<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added Artwork editing for songs</li>
|
||||
<li>Circle theme has album art now</li>
|
||||
<li>Upgraded tag editor library, we should support reading tags of more formats now e.g.
|
||||
opus.
|
||||
</li>
|
||||
<li>Added Snowfall effect</li>
|
||||
<li>Crossfade effect for background when Song is changed for Blur, Blur card themes</li>
|
||||
<li>Album art is hidden when Show lyrics is enabled</li>
|
||||
<li>Added fastscroll in Playlists tab</li>
|
||||
<li>Added Chooser to choose what to restore</li>
|
||||
<li>Hide BottomNavigation when only one tab is selected in Library Categories(This was
|
||||
already there but when changing screens it was getting visible)
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Fixed Ringtone crash</li>
|
||||
<li>Fixed blank album cover bug</li>
|
||||
<li>Fixed bottom navigation visible in Playing Queue</li>
|
||||
<li>Fixed lockscreen dragging glitch</li>
|
||||
<li>Fixed incorrect colors when no cover art is available</li>
|
||||
<li>Fixed favorite not updating when song is changed</li>
|
||||
<li>Fixed playlist not getting created & playlist creation crash with same name</li>
|
||||
<li>Fixed a bug in "Plain" Now playing theme where onClick event is consumed by the views
|
||||
behind the bottom sheet
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>December 6, 2021</h5>
|
||||
<h2>v5.4.2<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li>Bug Fixes</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>December 1, 2021</h5>
|
||||
<h2>v5.4.1<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added file selection from system file picker for restore</li>
|
||||
<li>Added Grid size selection for Playlists</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Enable Material You by default on Android 12</li>
|
||||
<li>Reworked Grid Style saving</li>
|
||||
<li>Improved Playlist preview images</li>
|
||||
<li>UI improvements</li>
|
||||
</ul>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Fixed File deletion on Android 10</li>
|
||||
<li>Fixed Sleep Timer crash</li>
|
||||
<li>Fixed Bottom Toolbar not clickable in now playing when Shuffle is clicked</li>
|
||||
<li>Fixed Album Artist sort order</li>
|
||||
<li>Fixed a crash when clicking the "Clear All" button in the Blacklist folder selection
|
||||
</li>
|
||||
<li>Fixed Continuous crashes on A12 when the song ends</li>
|
||||
<li>Fixed New Music Mix multiple clicks crash</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>November 22, 2021</h5>
|
||||
<h2>v5.4.0<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
|
|
|
@ -152,3 +152,4 @@ const val REMEMBER_LAST_TAB = "remember_last_tab"
|
|||
const val LAST_USED_TAB = "last_used_tab"
|
||||
const val WHITELIST_MUSIC = "whitelist_music"
|
||||
const val MATERIAL_YOU = "material_you"
|
||||
const val SNOWFALL = "snowfall"
|
||||
|
|
|
@ -15,16 +15,19 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityDriveModeBinding
|
||||
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
|
@ -35,13 +38,15 @@ import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
|||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
|
||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
||||
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.repository.RealRepository
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
|
||||
/**
|
||||
|
@ -54,9 +59,9 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
private var lastPlaybackControlsColor: Int = Color.GRAY
|
||||
private var lastDisabledPlaybackControlsColor: Int = Color.GRAY
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
private val repository: RealRepository by inject()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityDriveModeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
@ -67,6 +72,7 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
binding.close.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
binding.repeatButton.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
|
@ -80,19 +86,32 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
|
||||
private fun setupFavouriteToggle() {
|
||||
binding.songFavourite.setOnClickListener {
|
||||
MusicUtil.toggleFavorite(
|
||||
this@DriveModeActivity,
|
||||
MusicPlayerRemote.currentSong
|
||||
)
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleFavourite() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val isFavourite =
|
||||
MusicUtil.isFavorite(this@DriveModeActivity, MusicPlayerRemote.currentSong)
|
||||
private fun toggleFavorite(song: Song) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val playlist = repository.favoritePlaylist()
|
||||
if (playlist != null) {
|
||||
val songEntity = song.toSongEntity(playlist.playListId)
|
||||
val isFavorite = repository.isSongFavorite(song.id)
|
||||
if (isFavorite) {
|
||||
repository.removeSongFromPlaylist(songEntity)
|
||||
} else {
|
||||
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
|
||||
}
|
||||
}
|
||||
sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFavorite() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val isFavorite: Boolean =
|
||||
repository.isSongFavorite(MusicPlayerRemote.currentSong.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.songFavourite.setImageResource(if (isFavourite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
|
||||
binding.songFavourite.setImageResource(if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +179,7 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
updateSong()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
toggleFavourite()
|
||||
updateFavorite()
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
|
@ -213,7 +232,12 @@ class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
toggleFavourite()
|
||||
updateFavorite()
|
||||
}
|
||||
|
||||
override fun onFavoriteStateChanged() {
|
||||
super.onFavoriteStateChanged()
|
||||
updateFavorite()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
|
|
|
@ -16,47 +16,41 @@ package code.name.monkey.retromusic.activities
|
|||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.webkit.WebView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityLicenseBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
/** Created by hemanths on 2019-09-27. */
|
||||
class LicenseActivity : AbsThemeActivity() {
|
||||
private lateinit var binding: ActivityLicenseBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_license)
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
toolbar.setBackgroundColor(resolveColor(this, R.attr.colorSurface))
|
||||
val webView = findViewById<WebView>(R.id.license)
|
||||
binding = ActivityLicenseBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
try {
|
||||
val buf = StringBuilder()
|
||||
val json = assets.open("oldindex.html")
|
||||
val br = BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8))
|
||||
var str: String?
|
||||
while (br.readLine().also { str = it } != null) {
|
||||
buf.append(str)
|
||||
val json = assets.open("license.html")
|
||||
BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)).use { br ->
|
||||
var str: String?
|
||||
while (br.readLine().also { str = it } != null) {
|
||||
buf.append(str)
|
||||
}
|
||||
}
|
||||
br.close()
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
val isDark = isWindowBackgroundDark(this)
|
||||
val backgroundColor = colorToCSS(
|
||||
resolveColor(
|
||||
this,
|
||||
R.attr.colorSurface,
|
||||
Color.parseColor(if (isDark) "#424242" else "#ffffff")
|
||||
)
|
||||
surfaceColor(Color.parseColor(if (isDark) "#424242" else "#ffffff"))
|
||||
)
|
||||
val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000"))
|
||||
val changeLog = buf.toString()
|
||||
|
@ -72,12 +66,13 @@ class LicenseActivity : AbsThemeActivity() {
|
|||
lightenColor(accentColor(this))
|
||||
)
|
||||
)
|
||||
webView.loadData(changeLog, "text/html", "UTF-8")
|
||||
binding.license.loadData(changeLog, "text/html", "UTF-8")
|
||||
} catch (e: Throwable) {
|
||||
webView.loadData(
|
||||
binding.license.loadData(
|
||||
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8"
|
||||
)
|
||||
}
|
||||
binding.license.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
|
|
@ -18,9 +18,9 @@ import android.app.KeyguardManager
|
|||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.ViewCompat
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityLockScreenBinding
|
||||
|
@ -41,13 +41,11 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
|||
private var fragment: LockScreenControlsFragment? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
lockScreenInit()
|
||||
binding = ActivityLockScreenBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
hideStatusBar()
|
||||
setStatusBarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
val config = SlidrConfig.Builder().listener(object : SlidrListener {
|
||||
|
@ -61,7 +59,7 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
|
||||
override fun onSlideClosed(): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (VersionUtils.hasOreo()) {
|
||||
val keyguardManager =
|
||||
getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
keyguardManager.requestDismissKeyguard(this@LockScreenActivity, null)
|
||||
|
@ -75,7 +73,7 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
|||
|
||||
fragment = whichFragment<LockScreenControlsFragment>(R.id.playback_controls_fragment)
|
||||
|
||||
findViewById<View>(R.id.slide).apply {
|
||||
binding.slide.apply {
|
||||
translationY = 100f
|
||||
alpha = 0f
|
||||
ViewCompat.animate(this).translationY(0f).alpha(1f).setDuration(1500).start()
|
||||
|
|
|
@ -27,15 +27,15 @@ import code.name.monkey.retromusic.*
|
|||
import code.name.monkey.retromusic.activities.base.AbsCastActivity
|
||||
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
||||
import code.name.monkey.retromusic.fragments.home.HomeFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
|
||||
import code.name.monkey.retromusic.interfaces.IScrollHelper
|
||||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.AppRater
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -52,7 +52,6 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setTaskDescriptionColorAuto()
|
||||
hideStatusBar()
|
||||
|
@ -63,6 +62,9 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
if (!hasPermissions()) {
|
||||
findNavController(R.id.fragment_container).navigate(R.id.permissionFragment)
|
||||
}
|
||||
if (BuildConfig.VERSION_CODE > PreferenceUtil.lastVersion){
|
||||
NavigationUtil.gotoWhatNews(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupNavigationController() {
|
||||
|
@ -90,21 +92,17 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
bottomNavigationView.setupWithNavController(navController)
|
||||
// Scroll Fragment to top
|
||||
bottomNavigationView.setOnItemReselectedListener {
|
||||
currentFragment(R.id.fragment_container)
|
||||
.also {
|
||||
if (it is AbsRecyclerViewFragment<*, *>) {
|
||||
it.scrollToTop()
|
||||
}
|
||||
if (it is HomeFragment) {
|
||||
it.scrollToTop()
|
||||
}
|
||||
currentFragment(R.id.fragment_container).apply {
|
||||
if (this is IScrollHelper) {
|
||||
scrollToTop()
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is more like a work-around as for start destination of navGraph
|
||||
// enterTransition won't work as expected
|
||||
navGraph.setStartDestination(R.id.libraryFragment)
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
when (destination.id) {
|
||||
if (destination.id == navGraph.startDestinationId) {
|
||||
currentFragment(R.id.fragment_container)?.enterTransition = null
|
||||
}
|
||||
when (destination.id) {
|
||||
R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre -> {
|
||||
// Save the last tab
|
||||
if (PreferenceUtil.rememberLastTab) {
|
||||
|
@ -114,10 +112,12 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
setBottomNavVisibility(visible = true, animate = true)
|
||||
}
|
||||
R.id.playing_queue_fragment -> {
|
||||
setBottomNavVisibility(visible = false)
|
||||
hideBottomSheet(true)
|
||||
setBottomNavVisibility(visible = false, hideBottomSheet = true)
|
||||
}
|
||||
else -> setBottomNavVisibility(visible = false, animate = true) // Hide Bottom Navigation Bar
|
||||
else -> setBottomNavVisibility(
|
||||
visible = false,
|
||||
animate = true
|
||||
) // Hide Bottom Navigation Bar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ import android.net.Uri
|
|||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.isVisible
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
|
@ -84,12 +84,12 @@ class PermissionActivity : AbsMusicServiceActivity() {
|
|||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
override fun onResume() {
|
||||
if (hasStoragePermission()) {
|
||||
binding.storagePermission.checkImage.visibility = View.VISIBLE
|
||||
binding.storagePermission.checkImage.isVisible = true
|
||||
binding.storagePermission.checkImage.imageTintList =
|
||||
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
}
|
||||
if (hasAudioPermission()) {
|
||||
binding.audioPermission.checkImage.visibility = View.VISIBLE
|
||||
binding.audioPermission.checkImage.isVisible = true
|
||||
binding.audioPermission.checkImage.imageTintList =
|
||||
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
|
|||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityProVersionBinding
|
||||
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
|
||||
import code.name.monkey.retromusic.extensions.setLightStatusBar
|
||||
import code.name.monkey.retromusic.extensions.setStatusBarColor
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
|
@ -40,7 +39,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
|||
private lateinit var billingProcessor: BillingProcessor
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityProVersionBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
|
|
@ -32,15 +32,18 @@ import com.afollestad.materialdialogs.color.ColorCallback
|
|||
class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListener {
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
val mSavedInstanceState = extra<Bundle>(TAG).value ?: savedInstanceState
|
||||
super.onCreate(mSavedInstanceState)
|
||||
setLightStatusBarAuto(surfaceColor())
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setupToolbar()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setNavigationBarColorPreOreo(surfaceColor())
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
applyToolbar(binding.toolbar)
|
||||
val navController: NavController = findNavController(R.id.contentFrame)
|
||||
|
@ -82,7 +85,6 @@ class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListen
|
|||
ThemeStore.editTheme(this).accentColor(color).commit()
|
||||
if (VersionUtils.hasNougatMR())
|
||||
DynamicShortcutManager(this).updateDynamicShortcuts()
|
||||
|
||||
restart()
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ import code.name.monkey.appthemehelper.util.ColorUtil
|
|||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityShareInstagramBinding
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
|
||||
import code.name.monkey.retromusic.extensions.setLightStatusBar
|
||||
import code.name.monkey.retromusic.extensions.setStatusBarColor
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
|
@ -60,7 +58,6 @@ class ShareInstagramStory : AbsBaseActivity() {
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityShareInstagramBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
|
|
@ -37,10 +37,7 @@ import code.name.monkey.retromusic.BuildConfig
|
|||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityDonationBinding
|
||||
import code.name.monkey.retromusic.extensions.setStatusBarColorAuto
|
||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.PurchaseInfo
|
||||
import com.anjlab.android.iab.v3.SkuDetails
|
||||
|
@ -86,7 +83,7 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
|
|||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||
val toolbarColor = surfaceColor()
|
||||
binding.toolbar.setBackgroundColor(toolbarColor)
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
|
|
@ -7,18 +7,14 @@ import android.os.Bundle
|
|||
import androidx.core.widget.NestedScrollView
|
||||
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil.isColorLight
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper.getPrimaryTextColor
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.Constants
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityWhatsNewBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.setLightStatusBarAuto
|
||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import java.io.BufferedReader
|
||||
|
@ -31,29 +27,25 @@ class WhatsNewActivity : AbsThemeActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
val binding = ActivityWhatsNewBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setLightStatusBarAuto(resolveColor(this, R.attr.colorSurface))
|
||||
setLightStatusBarAuto(surfaceColor())
|
||||
setTaskDescriptionColorAuto()
|
||||
binding.toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
try {
|
||||
val buf = StringBuilder()
|
||||
val json = assets.open("retro-changelog.html")
|
||||
val br = BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8))
|
||||
var str: String?
|
||||
while (br.readLine().also { str = it } != null) {
|
||||
buf.append(str)
|
||||
BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)).use { br ->
|
||||
var str: String?
|
||||
while (br.readLine().also { str = it } != null) {
|
||||
buf.append(str)
|
||||
}
|
||||
}
|
||||
br.close()
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
val isDark = isWindowBackgroundDark(this)
|
||||
val accentColor = accentColor(this)
|
||||
val backgroundColor = colorToCSS(
|
||||
resolveColor(
|
||||
this,
|
||||
R.attr.colorSurface,
|
||||
Color.parseColor(if (isDark) "#424242" else "#ffffff")
|
||||
)
|
||||
surfaceColor(Color.parseColor(if (isDark) "#424242" else "#ffffff"))
|
||||
)
|
||||
val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000"))
|
||||
val textColor = colorToCSS(Color.parseColor(if (isDark) "#60FFFFFF" else "#80000000"))
|
||||
|
@ -100,6 +92,7 @@ class WhatsNewActivity : AbsThemeActivity() {
|
|||
binding.tgFab.extend()
|
||||
}
|
||||
}
|
||||
binding.webView.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -14,21 +14,20 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.animation.PathInterpolator
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.*
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.RetroBottomSheetBehavior
|
||||
|
@ -61,9 +60,11 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
|||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
|
||||
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
||||
companion object {
|
||||
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
||||
|
@ -78,21 +79,34 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
private var nowPlayingScreen: NowPlayingScreen? = null
|
||||
private var taskColor: Int = 0
|
||||
private var paletteColor: Int = Color.WHITE
|
||||
private var navigationBarColor = 0
|
||||
protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding
|
||||
private val panelState: Int
|
||||
get() = bottomSheetBehavior.state
|
||||
private lateinit var binding: SlidingMusicPanelLayoutBinding
|
||||
private var isInOneTabMode = false
|
||||
|
||||
private var navigationBarColorAnimator: ValueAnimator? = null
|
||||
private val argbEvaluator: ArgbEvaluator = ArgbEvaluator()
|
||||
|
||||
private val bottomSheetCallbackList = object : BottomSheetCallback() {
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
setMiniPlayerAlphaProgress(slideOffset)
|
||||
navigationBarColorAnimator?.cancel()
|
||||
setNavigationBarColorPreOreo(
|
||||
argbEvaluator.evaluate(
|
||||
slideOffset,
|
||||
surfaceColor(),
|
||||
navigationBarColor
|
||||
) as Int
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
when (newState) {
|
||||
STATE_EXPANDED -> {
|
||||
onPanelExpanded()
|
||||
|
||||
}
|
||||
STATE_COLLAPSED -> {
|
||||
onPanelCollapsed()
|
||||
|
@ -122,7 +136,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
windowInsets = insets
|
||||
insets
|
||||
}
|
||||
bottomNavigationView.drawAboveSystemBarsWithPadding()
|
||||
if (RetroUtil.isLandscape()) {
|
||||
binding.slidingPanel.drawAboveSystemBarsWithPadding(true)
|
||||
}
|
||||
|
@ -132,6 +145,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
updateColor()
|
||||
binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
||||
bottomNavigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
||||
navigationBarColor = surfaceColor()
|
||||
}
|
||||
|
||||
private fun setupBottomSheet() {
|
||||
|
@ -178,9 +192,25 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
|
||||
}
|
||||
|
||||
private fun animateNavigationBarColor(color: Int) {
|
||||
navigationBarColorAnimator?.cancel()
|
||||
navigationBarColorAnimator = ValueAnimator
|
||||
.ofArgb(window.navigationBarColor, color).apply {
|
||||
duration = ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong()
|
||||
interpolator = PathInterpolator(0.4f, 0f, 1f, 1f)
|
||||
addUpdateListener { animation: ValueAnimator ->
|
||||
setNavigationBarColorPreOreo(
|
||||
animation.animatedValue as Int
|
||||
)
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
open fun onPanelCollapsed() {
|
||||
setMiniPlayerAlphaProgress(0F)
|
||||
// restore values
|
||||
animateNavigationBarColor(surfaceColor())
|
||||
setLightStatusBarAuto(surfaceColor())
|
||||
setLightNavigationAuto()
|
||||
setTaskDescriptionColor(taskColor)
|
||||
|
@ -233,9 +263,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
super.onQueueChanged()
|
||||
// Mini player should be hidden in Playing Queue
|
||||
// it may pop up if hideBottomSheet is called
|
||||
if (currentFragment(R.id.fragment_container) !is PlayingQueueFragment &&
|
||||
bottomSheetBehavior.state != STATE_EXPANDED
|
||||
) {
|
||||
if (currentFragment(R.id.fragment_container) !is PlayingQueueFragment) {
|
||||
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
}
|
||||
}
|
||||
|
@ -255,18 +283,25 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
|
||||
private fun onPaletteColorChanged() {
|
||||
if (panelState == STATE_EXPANDED) {
|
||||
navigationBarColor = surfaceColor()
|
||||
setTaskDescColor(paletteColor)
|
||||
val isColorLight = ColorUtil.isColorLight(paletteColor)
|
||||
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat)) {
|
||||
setLightNavigationBar(true)
|
||||
setLightStatusBar(isColorLight)
|
||||
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
|
||||
animateNavigationBarColor(Color.BLACK)
|
||||
navigationBarColor = Color.BLACK
|
||||
setLightStatusBar(false)
|
||||
setLightNavigationBar(true)
|
||||
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
|
||||
animateNavigationBarColor(paletteColor)
|
||||
navigationBarColor = paletteColor
|
||||
setLightNavigationBar(isColorLight)
|
||||
setLightStatusBar(isColorLight)
|
||||
} else if (nowPlayingScreen == Full) {
|
||||
animateNavigationBarColor(paletteColor)
|
||||
navigationBarColor = paletteColor
|
||||
setLightNavigationBar(isColorLight)
|
||||
setLightStatusBar(false)
|
||||
} else if (nowPlayingScreen == Classic) {
|
||||
|
@ -276,10 +311,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
} else {
|
||||
setLightStatusBar(
|
||||
ColorUtil.isColorLight(
|
||||
ATHUtil.resolveColor(
|
||||
this,
|
||||
android.R.attr.windowBackground
|
||||
)
|
||||
surfaceColor()
|
||||
)
|
||||
)
|
||||
setLightNavigationBar(true)
|
||||
|
@ -305,6 +337,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
}
|
||||
if (binding.bottomNavigationView.menu.size() == 1) {
|
||||
isInOneTabMode = true
|
||||
binding.bottomNavigationView.hide()
|
||||
}
|
||||
}
|
||||
|
@ -316,10 +349,19 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
})
|
||||
}
|
||||
|
||||
fun setBottomNavVisibility(visible: Boolean, animate: Boolean = false) {
|
||||
fun setBottomNavVisibility(visible: Boolean, animate: Boolean = false, hideBottomSheet: Boolean = MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||
if (isInOneTabMode) {
|
||||
hideBottomSheet(
|
||||
hide = hideBottomSheet,
|
||||
animate = animate,
|
||||
isBottomNavVisible = false
|
||||
)
|
||||
return
|
||||
}
|
||||
val translationY =
|
||||
if (visible) 0F else dip(R.dimen.bottom_nav_height).toFloat() + windowInsets.safeGetBottomInsets()
|
||||
if (animate) {
|
||||
val mAnimate = animate && bottomSheetBehavior.state == STATE_COLLAPSED
|
||||
if (mAnimate) {
|
||||
binding.bottomNavigationView.translateYAnimate(translationY).doOnEnd {
|
||||
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
|
||||
binding.bottomNavigationView.bringToFront()
|
||||
|
@ -328,12 +370,13 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
} else {
|
||||
binding.bottomNavigationView.translationY =
|
||||
translationY
|
||||
binding.bottomNavigationView.isVisible = false
|
||||
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
|
||||
binding.bottomNavigationView.bringToFront()
|
||||
}
|
||||
}
|
||||
hideBottomSheet(
|
||||
hide = MusicPlayerRemote.playingQueue.isEmpty(),
|
||||
hide = hideBottomSheet,
|
||||
animate = animate,
|
||||
isBottomNavVisible = visible
|
||||
)
|
||||
|
|
|
@ -35,6 +35,7 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
|||
private val handler = Handler()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawBehindSystemBars()
|
||||
updateTheme()
|
||||
hideStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -34,7 +34,6 @@ import androidx.appcompat.app.AlertDialog
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
|
@ -60,14 +59,13 @@ import java.io.File
|
|||
import java.util.*
|
||||
|
||||
abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
||||
abstract val editorImage: ImageView?
|
||||
abstract val editorImage: ImageView
|
||||
val repository by inject<Repository>()
|
||||
|
||||
lateinit var saveFab: MaterialButton
|
||||
protected var id: Long = 0
|
||||
private set
|
||||
private var paletteColorPrimary: Int = 0
|
||||
private var isInNoImageMode: Boolean = false
|
||||
private var songPaths: List<String>? = null
|
||||
private var savedSongPaths: List<String>? = null
|
||||
private val currentSongPath: String? = null
|
||||
|
@ -176,6 +174,15 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
protected val discNumber: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val lyrics: String?
|
||||
get() {
|
||||
return try {
|
||||
|
@ -239,7 +246,7 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
getString(R.string.web_search),
|
||||
getString(R.string.remove_cover)
|
||||
)
|
||||
editorImage?.setOnClickListener { show }
|
||||
editorImage.setOnClickListener { show }
|
||||
}
|
||||
|
||||
private fun startImagePicker() {
|
||||
|
@ -306,17 +313,6 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
protected fun setNoImageMode() {
|
||||
isInNoImageMode = true
|
||||
setColors(
|
||||
intent.getIntExtra(
|
||||
EXTRA_PALETTE,
|
||||
ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
protected fun dataChanged() {
|
||||
showFab()
|
||||
}
|
||||
|
@ -335,9 +331,9 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
|
||||
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
||||
if (bitmap == null) {
|
||||
editorImage?.setImageResource(drawable.default_audio_art)
|
||||
editorImage.setImageResource(drawable.default_audio_art)
|
||||
} else {
|
||||
editorImage?.setImageBitmap(bitmap)
|
||||
editorImage.setImageBitmap(bitmap)
|
||||
}
|
||||
setColors(bgColor)
|
||||
}
|
||||
|
|
|
@ -28,11 +28,12 @@ import android.transition.Slide
|
|||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.extensions.setDrawUnderStatusBar
|
||||
import code.name.monkey.retromusic.extensions.defaultFooterColor
|
||||
import code.name.monkey.retromusic.extensions.isColorLight
|
||||
import code.name.monkey.retromusic.extensions.setTint
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
|
@ -64,41 +65,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
window.enterTransition = slide
|
||||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
getColor(resource.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||
setImageBitmap(
|
||||
albumArtBitmap,
|
||||
getColor(
|
||||
resource.palette,
|
||||
ATHUtil.resolveColor(
|
||||
this@AlbumTagEditorActivity,
|
||||
R.attr.defaultFooterColor
|
||||
)
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
Toast.makeText(this@AlbumTagEditorActivity, "Load Failed", Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
||||
})
|
||||
}
|
||||
|
||||
private var albumArtBitmap: Bitmap? = null
|
||||
private var deleteAlbumArt: Boolean = false
|
||||
|
||||
|
@ -109,7 +75,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
window.sharedElementsUseOverlay = true
|
||||
binding.imageContainer.transitionName = getString(R.string.transition_album_art)
|
||||
|
@ -146,7 +111,7 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
bitmap,
|
||||
getColor(
|
||||
generatePalette(bitmap),
|
||||
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
|
@ -167,12 +132,44 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
override fun deleteImage() {
|
||||
setImageBitmap(
|
||||
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
|
||||
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
|
||||
defaultFooterColor()
|
||||
)
|
||||
deleteAlbumArt = true
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
getColor(resource.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||
setImageBitmap(
|
||||
albumArtBitmap,
|
||||
getColor(
|
||||
resource.palette,
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
Toast.makeText(this@AlbumTagEditorActivity, "Load Failed", Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
||||
})
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
|
||||
|
@ -215,6 +212,16 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
override fun setColors(color: Int) {
|
||||
super.setColors(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
color.isColorLight
|
||||
)
|
||||
).also {
|
||||
saveFab.iconTint = it
|
||||
saveFab.setTextColor(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,17 +15,36 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.extensions.defaultFooterColor
|
||||
import code.name.monkey.retromusic.extensions.isColorLight
|
||||
import code.name.monkey.retromusic.extensions.setTint
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||
import code.name.monkey.retromusic.repository.SongRepository
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.target.ImageViewTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import org.koin.android.ext.android.inject
|
||||
|
@ -39,12 +58,14 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
|
||||
private val songRepository by inject<SongRepository>()
|
||||
|
||||
private var albumArtBitmap: Bitmap? = null
|
||||
private var deleteAlbumArt: Boolean = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setUpViews()
|
||||
setNoImageMode()
|
||||
setSupportActionBar(binding.toolbar)
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
binding.appBarLayout?.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(this)
|
||||
}
|
||||
|
||||
|
@ -59,6 +80,7 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
binding.yearContainer.setTint(false)
|
||||
binding.genreContainer.setTint(false)
|
||||
binding.trackNumberContainer.setTint(false)
|
||||
binding.discNumberContainer.setTint(false)
|
||||
binding.lyricsContainer.setTint(false)
|
||||
|
||||
binding.songText.appHandleColor().addTextChangedListener(this)
|
||||
|
@ -68,13 +90,9 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
binding.genreText.appHandleColor().addTextChangedListener(this)
|
||||
binding.yearText.appHandleColor().addTextChangedListener(this)
|
||||
binding.trackNumberText.appHandleColor().addTextChangedListener(this)
|
||||
binding.discNumberText.appHandleColor().addTextChangedListener(this)
|
||||
binding.lyricsText.appHandleColor().addTextChangedListener(this)
|
||||
binding.songComposerText.appHandleColor().addTextChangedListener(this)
|
||||
|
||||
binding.lyricsText.setOnTouchListener { view, _ ->
|
||||
view.parent.requestDisallowInterceptTouchEvent(true)
|
||||
return@setOnTouchListener false
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillViewsWithFileTags() {
|
||||
|
@ -85,16 +103,50 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
binding.genreText.setText(genreName)
|
||||
binding.yearText.setText(songYear)
|
||||
binding.trackNumberText.setText(trackNumber)
|
||||
binding.discNumberText.setText(discNumber)
|
||||
binding.lyricsText.setText(lyrics)
|
||||
binding.songComposerText.setText(composer)
|
||||
println(songTitle + songYear)
|
||||
}
|
||||
|
||||
override fun loadCurrentImage() {}
|
||||
override fun loadCurrentImage() {
|
||||
val bitmap = albumArt
|
||||
setImageBitmap(
|
||||
bitmap,
|
||||
RetroColorUtil.getColor(
|
||||
RetroColorUtil.generatePalette(bitmap),
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
}
|
||||
|
||||
override fun searchImageOnWeb() {}
|
||||
override fun searchImageOnWeb() {
|
||||
searchWebFor(binding.songText.text.toString(), binding.artistText.text.toString())
|
||||
}
|
||||
|
||||
override fun deleteImage() {}
|
||||
override fun deleteImage() {
|
||||
setImageBitmap(
|
||||
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
|
||||
defaultFooterColor()
|
||||
)
|
||||
deleteAlbumArt = true
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
override fun setColors(color: Int) {
|
||||
super.setColors(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
color.isColorLight
|
||||
)
|
||||
).also {
|
||||
saveFab.iconTint = it
|
||||
saveFab.setTextColor(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
|
@ -104,10 +156,17 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
fieldKeyValueMap[FieldKey.GENRE] = binding.genreText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = binding.yearText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.TRACK] = binding.trackNumberText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.DISC_NO] = binding.discNumberText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = binding.lyricsText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.COMPOSER] = binding.songComposerText.text.toString()
|
||||
writeValuesToFiles(fieldKeyValueMap, null)
|
||||
writeValuesToFiles(
|
||||
fieldKeyValueMap, when {
|
||||
deleteAlbumArt -> ArtworkInfo(id, null)
|
||||
albumArtBitmap == null -> null
|
||||
else -> ArtworkInfo(id, albumArtBitmap!!)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSongPaths(): List<String> = listOf(songRepository.song(id).data)
|
||||
|
@ -115,6 +174,35 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
override fun getSongUris(): List<Uri> = listOf(MusicUtil.getSongFileUri(id))
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
GlideApp.with(this@SongTagEditorActivity).asBitmapPalette().load(selectedFile)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||
setImageBitmap(
|
||||
albumArtBitmap,
|
||||
RetroColorUtil.getColor(
|
||||
resource.palette,
|
||||
defaultFooterColor()
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
Toast.makeText(this@SongTagEditorActivity, "Load Failed", Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
||||
})
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
|
@ -131,6 +219,6 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
override val editorImage: ImageView?
|
||||
get() = null
|
||||
override val editorImage: ImageView
|
||||
get() = binding.editorImage
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ import org.jaudiotagger.audio.exceptions.CannotWriteException
|
|||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
||||
import org.jaudiotagger.tag.TagException
|
||||
import org.jaudiotagger.tag.images.AndroidArtwork
|
||||
import org.jaudiotagger.tag.images.Artwork
|
||||
import org.jaudiotagger.tag.images.ArtworkFactory
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
@ -58,11 +58,11 @@ class TagWriter {
|
|||
try {
|
||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
||||
info.artworkInfo.artwork.compress(
|
||||
Bitmap.CompressFormat.PNG,
|
||||
0,
|
||||
Bitmap.CompressFormat.JPEG,
|
||||
100,
|
||||
FileOutputStream(albumArtFile)
|
||||
)
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile)
|
||||
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
@ -131,11 +131,11 @@ class TagWriter {
|
|||
try {
|
||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
||||
info.artworkInfo.artwork.compress(
|
||||
Bitmap.CompressFormat.PNG,
|
||||
0,
|
||||
Bitmap.CompressFormat.JPEG,
|
||||
100,
|
||||
FileOutputStream(albumArtFile)
|
||||
)
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile)
|
||||
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
|
|
@ -33,9 +33,10 @@ 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.helper.menu.SongMenuHelper
|
||||
import code.name.monkey.retromusic.model.*
|
||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
||||
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import java.util.*
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.fragments.base.goToLyrics
|
||||
|
@ -36,7 +35,6 @@ import code.name.monkey.retromusic.model.Song
|
|||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -91,7 +89,6 @@ class AlbumCoverPagerAdapter(
|
|||
private lateinit var song: Song
|
||||
private var colorReceiver: ColorReceiver? = null
|
||||
private var request: Int = 0
|
||||
private val mainActivity get() = activity as MainActivity
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -108,11 +105,6 @@ class AlbumCoverPagerAdapter(
|
|||
val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false)
|
||||
ViewCompat.setTransitionName(view, "lyrics")
|
||||
albumCover = view.findViewById(R.id.player_image)
|
||||
view.setOnClickListener {
|
||||
if (mainActivity.getBottomSheetBehavior().state == STATE_EXPANDED) {
|
||||
showLyricsDialog()
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
|
@ -127,8 +119,7 @@ class AlbumCoverPagerAdapter(
|
|||
setTitle(song.title)
|
||||
setMessage(if (data.isNullOrEmpty()) "No lyrics found" else data)
|
||||
setNegativeButton(R.string.synced_lyrics) { _, _ ->
|
||||
|
||||
goToLyrics(requireActivity())
|
||||
goToLyrics(requireActivity())
|
||||
}
|
||||
show()
|
||||
}
|
||||
|
@ -139,6 +130,7 @@ class AlbumCoverPagerAdapter(
|
|||
private fun getLayoutWithPlayerTheme(): Int {
|
||||
return when (PreferenceUtil.nowPlayingScreen) {
|
||||
Card, Fit, Tiny, Classic, Gradient, Full -> R.layout.fragment_album_full_cover
|
||||
Peak -> R.layout.fragment_peak_album_cover
|
||||
else -> {
|
||||
if (PreferenceUtil.isCarouselEffect) {
|
||||
R.layout.fragment_album_carousel_cover
|
||||
|
|
|
@ -4,9 +4,9 @@ import android.view.LayoutInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
|
|
|
@ -7,8 +7,8 @@ import android.view.MenuItem
|
|||
import androidx.annotation.MenuRes
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
|
@ -25,7 +25,7 @@ abstract class AbsMultiSelectAdapter<V : RecyclerView.ViewHolder?, I>(
|
|||
private var menuRes: Int
|
||||
override fun onCabCreated(cab: AttachedCab, menu: Menu): Boolean {
|
||||
activity.window.statusBarColor =
|
||||
RetroColorUtil.shiftBackgroundColor(ATHUtil.resolveColor(activity, R.attr.colorSurface))
|
||||
RetroColorUtil.shiftBackgroundColor(activity.surfaceColor())
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -35,12 +35,15 @@ import code.name.monkey.retromusic.extensions.hide
|
|||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview
|
||||
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder
|
||||
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
class PlaylistAdapter(
|
||||
override val activity: FragmentActivity,
|
||||
|
@ -52,7 +55,7 @@ class PlaylistAdapter(
|
|||
activity,
|
||||
ICabHolder,
|
||||
R.menu.menu_playlists_selection
|
||||
) {
|
||||
), PopupTextProvider {
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
|
@ -84,6 +87,17 @@ class PlaylistAdapter(
|
|||
return MusicUtil.getPlaylistInfoString(activity, playlist.songs.toSongs())
|
||||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
val sectionName: String = when (PreferenceUtil.playlistSortOrder) {
|
||||
PlaylistSortOrder.PLAYLIST_A_Z, PlaylistSortOrder.PLAYLIST_Z_A -> dataSet[position].playlistEntity.playlistName
|
||||
PlaylistSortOrder.PLAYLIST_SONG_COUNT, PlaylistSortOrder.PLAYLIST_SONG_COUNT_DESC -> dataSet[position].songs.size.toString()
|
||||
else -> {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return MusicUtil.getSectionName(sectionName)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val playlist = dataSet[position]
|
||||
holder.itemView.isActivated = isChecked(playlist)
|
||||
|
|
|
@ -35,7 +35,7 @@ class CastOptionsProvider : OptionsProvider {
|
|||
.build()
|
||||
}
|
||||
|
||||
override fun getAdditionalSessionProviders(context: Context?): List<SessionProvider>? {
|
||||
override fun getAdditionalSessionProviders(context: Context): MutableList<SessionProvider>? {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ interface PlaylistDao {
|
|||
suspend fun renamePlaylist(playlistId: Long, name: String)
|
||||
|
||||
@Query("SELECT * FROM PlaylistEntity WHERE playlist_name = :name")
|
||||
fun isPlaylistExists(name: String): List<PlaylistEntity>
|
||||
fun playlist(name: String): List<PlaylistEntity>
|
||||
|
||||
@Query("SELECT * FROM PlaylistEntity")
|
||||
suspend fun playlists(): List<PlaylistEntity>
|
||||
|
|
|
@ -65,9 +65,8 @@ class CreatePlaylistDialog : DialogFragment() {
|
|||
val playlistName = playlistView.text.toString()
|
||||
if (!TextUtils.isEmpty(playlistName)) {
|
||||
libraryViewModel.addToPlaylist(playlistName, songs)
|
||||
|
||||
} else {
|
||||
playlistContainer.error = "Playlist is can't be empty"
|
||||
playlistContainer.error = "Playlist name can't be empty"
|
||||
}
|
||||
}
|
||||
.create()
|
||||
|
|
|
@ -30,7 +30,11 @@ class ImportPlaylistDialog : DialogFragment() {
|
|||
return materialDialog(R.string.import_playlist)
|
||||
.setMessage(R.string.import_playlist_message)
|
||||
.setPositiveButton(R.string.import_label) { _, _ ->
|
||||
libraryViewModel.importPlaylists()
|
||||
try {
|
||||
libraryViewModel.importPlaylists()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
.create()
|
||||
.colorButtons()
|
||||
|
|
|
@ -14,14 +14,16 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.extensions
|
||||
|
||||
import android.R
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
|
||||
fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) {
|
||||
//toolbar.setBackgroundColor(surfaceColor())
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
|
@ -38,4 +40,6 @@ inline fun <reified T : Any> Activity.extraNotNull(key: String, default: T? = nu
|
|||
|
||||
fun Activity.dip(@DimenRes id: Int): Int {
|
||||
return resources.getDimensionPixelSize(id)
|
||||
}
|
||||
}
|
||||
|
||||
inline val Activity.rootView: View get() = findViewById<ViewGroup>(R.id.content).getChildAt(0)
|
|
@ -9,15 +9,13 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.appthemehelper.ATH
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
fun AppCompatActivity.toggleScreenOn() {
|
||||
if (PreferenceUtil.isScreenOnEnabled) {
|
||||
|
@ -43,12 +41,10 @@ fun AppCompatActivity.setImmersiveFullscreen() {
|
|||
|
||||
fun AppCompatActivity.exitFullscreen() {
|
||||
WindowInsetsControllerCompat(window, window.decorView).apply {
|
||||
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
show(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun AppCompatActivity.hideStatusBar() {
|
||||
hideStatusBar(PreferenceUtil.isFullScreenMode)
|
||||
}
|
||||
|
@ -56,13 +52,26 @@ fun AppCompatActivity.hideStatusBar() {
|
|||
private fun AppCompatActivity.hideStatusBar(fullscreen: Boolean) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
|
||||
statusBar.isGone = fullscreen
|
||||
}
|
||||
}
|
||||
|
||||
fun AppCompatActivity.setDrawUnderStatusBar() {
|
||||
fun AppCompatActivity.setDrawBehindSystemBars() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.statusBarColor = Color.TRANSPARENT
|
||||
if (VersionUtils.hasOreo()) {
|
||||
if (VersionUtils.hasQ()) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
window.navigationBarColor = Color.TRANSPARENT
|
||||
window.statusBarColor = Color.TRANSPARENT
|
||||
} else {
|
||||
setNavigationBarColorPreOreo(surfaceColor())
|
||||
if (VersionUtils.hasMarshmallow()) {
|
||||
setStatusBarColor(Color.TRANSPARENT)
|
||||
} else {
|
||||
setStatusBarColor(surfaceColor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun FragmentActivity.setTaskDescriptionColor(color: Int) {
|
||||
|
@ -100,9 +109,11 @@ fun AppCompatActivity.setLightStatusBarAuto(bgColor: Int) {
|
|||
}
|
||||
|
||||
fun AppCompatActivity.setLightNavigationBar(enabled: Boolean) {
|
||||
if (!ATHUtil.isWindowBackgroundDark(this) and ThemeStore.coloredNavigationBar(this)) {
|
||||
ATH.setLightNavigationbar(this, enabled)
|
||||
}
|
||||
ATH.setLightNavigationBar(this, enabled)
|
||||
}
|
||||
|
||||
fun AppCompatActivity.setLightNavigationBarAuto(bgColor: Int) {
|
||||
setLightNavigationBar(ColorUtil.isColorLight(bgColor))
|
||||
}
|
||||
|
||||
|
||||
|
@ -129,11 +140,39 @@ fun AppCompatActivity.setStatusBarColor(color: Int) {
|
|||
else -> window.statusBarColor = ColorUtil.darkenColor(color)
|
||||
}
|
||||
}
|
||||
setLightStatusBarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setLightStatusBarAuto(surfaceColor())
|
||||
}
|
||||
|
||||
fun AppCompatActivity.setStatusBarColorAuto() {
|
||||
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
||||
setStatusBarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setLightStatusBarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setStatusBarColor(surfaceColor())
|
||||
setLightStatusBarAuto(surfaceColor())
|
||||
}
|
||||
|
||||
fun AppCompatActivity.setNavigationBarColor(color: Int) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window.navigationBarColor = color
|
||||
} else {
|
||||
window.navigationBarColor = ColorUtil.darkenColor(color)
|
||||
}
|
||||
setLightNavigationBarAuto(color)
|
||||
}
|
||||
|
||||
fun AppCompatActivity.setNavigationBarColorPreOreo(color: Int) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
window.navigationBarColor = ColorUtil.darkenColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
fun AppCompatActivity.setStatusBarColorPreMarshmallow(color: Int) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
statusBar.setBackgroundColor(
|
||||
ColorUtil.darkenColor(
|
||||
color
|
||||
)
|
||||
)
|
||||
} else {
|
||||
window.statusBarColor = ColorUtil.darkenColor(color)
|
||||
}
|
||||
}
|
|
@ -65,6 +65,10 @@ fun Context.surfaceColor() = resolveColor(R.attr.colorSurface, Color.WHITE)
|
|||
|
||||
fun Fragment.surfaceColor() = resolveColor(R.attr.colorSurface, Color.WHITE)
|
||||
|
||||
fun Context.surfaceColor(fallBackColor: Int) = resolveColor(R.attr.colorSurface, fallBackColor)
|
||||
|
||||
fun Fragment.surfaceColor(fallBackColor: Int) = resolveColor(R.attr.colorSurface, fallBackColor)
|
||||
|
||||
fun Context.textColorSecondary() = resolveColor(android.R.attr.textColorSecondary)
|
||||
|
||||
fun Fragment.textColorSecondary() = resolveColor(android.R.attr.textColorSecondary)
|
||||
|
@ -77,6 +81,10 @@ fun Context.textColorPrimary() = resolveColor(android.R.attr.textColorPrimary)
|
|||
|
||||
fun Fragment.textColorPrimary() = resolveColor(android.R.attr.textColorPrimary)
|
||||
|
||||
fun Context.defaultFooterColor() = resolveColor(R.attr.defaultFooterColor)
|
||||
|
||||
fun Fragment.defaultFooterColor() = resolveColor(R.attr.defaultFooterColor)
|
||||
|
||||
fun Context.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) =
|
||||
ATHUtil.resolveColor(this, attr, fallBackColor)
|
||||
|
||||
|
@ -120,6 +128,15 @@ fun MaterialButton.accentOutlineColor() {
|
|||
rippleColor = colorStateList
|
||||
}
|
||||
|
||||
fun MaterialButton.elevatedAccentColor() {
|
||||
if (materialYou) return
|
||||
val color = context.darkAccentColorVariant()
|
||||
rippleColor = ColorStateList.valueOf(color)
|
||||
setBackgroundColor(color)
|
||||
setTextColor(MaterialValueHelper.getPrimaryTextColor(context, color.isColorLight))
|
||||
iconTint = ColorStateList.valueOf(context.accentColor())
|
||||
}
|
||||
|
||||
fun SeekBar.applyColor(@ColorInt color: Int) {
|
||||
thumbTintList = ColorStateList.valueOf(color)
|
||||
progressTintList = ColorStateList.valueOf(color)
|
||||
|
@ -234,5 +251,14 @@ fun Context.darkAccentColor(): Int {
|
|||
)
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Context.darkAccentColorVariant(): Int {
|
||||
return ColorUtils.blendARGB(
|
||||
accentColor(),
|
||||
surfaceColor(),
|
||||
if (surfaceColor().isColorLight) 0.9f else 0.95f
|
||||
)
|
||||
}
|
||||
|
||||
inline val @receiver:ColorInt Int.isColorLight
|
||||
get() = ColorUtil.isColorLight(this)
|
||||
|
|
|
@ -20,7 +20,7 @@ import android.database.Cursor
|
|||
|
||||
internal fun Cursor.getInt(columnName: String): Int {
|
||||
try {
|
||||
return this.getInt(this.getColumnIndex(columnName))
|
||||
return getInt(getColumnIndexOrThrow(columnName))
|
||||
} catch (ex: Throwable) {
|
||||
throw IllegalStateException("invalid column $columnName", ex)
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ internal fun Cursor.getInt(columnName: String): Int {
|
|||
|
||||
internal fun Cursor.getLong(columnName: String): Long {
|
||||
try {
|
||||
return this.getLong(this.getColumnIndex(columnName))
|
||||
return getLong(getColumnIndexOrThrow(columnName))
|
||||
} catch (ex: Throwable) {
|
||||
throw IllegalStateException("invalid column $columnName", ex)
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ internal fun Cursor.getLong(columnName: String): Long {
|
|||
|
||||
internal fun Cursor.getString(columnName: String): String {
|
||||
try {
|
||||
return this.getString(this.getColumnIndex(columnName))
|
||||
return getString(getColumnIndexOrThrow(columnName))
|
||||
} catch (ex: Throwable) {
|
||||
throw IllegalStateException("invalid column $columnName", ex)
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ internal fun Cursor.getString(columnName: String): String {
|
|||
|
||||
internal fun Cursor.getStringOrNull(columnName: String): String? {
|
||||
try {
|
||||
return this.getString(this.getColumnIndex(columnName))
|
||||
return getString(getColumnIndexOrThrow(columnName))
|
||||
} catch (ex: Throwable) {
|
||||
throw IllegalStateException("invalid column $columnName", ex)
|
||||
}
|
||||
|
|
|
@ -66,8 +66,7 @@ val FragmentManager.currentNavigationFragment: Fragment?
|
|||
fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? {
|
||||
val navHostFragment: NavHostFragment =
|
||||
supportFragmentManager.findFragmentById(navHostId) as NavHostFragment
|
||||
navHostFragment.targetFragment
|
||||
return navHostFragment.childFragmentManager.fragments.first()
|
||||
return navHostFragment.childFragmentManager.fragments.firstOrNull()
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
|
|
@ -2,7 +2,6 @@ package code.name.monkey.retromusic.extensions
|
|||
|
||||
import android.net.Uri
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package code.name.monkey.retromusic.extensions
|
||||
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
fun WindowInsetsCompat?.safeGetBottomInsets(): Int {
|
||||
// Get Navbar heights if insets are null
|
||||
return (this?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: RetroUtil.getNavigationBarHeight())
|
||||
return if (PreferenceUtil.isFullScreenMode) {
|
||||
return 0
|
||||
} else {
|
||||
this?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import code.name.monkey.retromusic.repository.RealRepository
|
|||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
@ -233,6 +234,7 @@ class LibraryViewModel(
|
|||
suspend fun artistById(id: Long) = repository.artistById(id)
|
||||
suspend fun favoritePlaylist() = repository.favoritePlaylist()
|
||||
suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song)
|
||||
suspend fun isSongFavorite(songId: Long) = repository.isSongFavorite(songId)
|
||||
suspend fun insertSongs(songs: List<SongEntity>) = repository.insertSongs(songs)
|
||||
suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
|
||||
repository.removeSongFromPlaylist(songEntity)
|
||||
|
@ -356,11 +358,21 @@ class LibraryViewModel(
|
|||
it.toSongEntity(playListId = playlist.playListId)
|
||||
})
|
||||
}
|
||||
Toast.makeText(
|
||||
App.getContext(),
|
||||
"Adding songs to $playlistName",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
withContext(Main) {
|
||||
Toast.makeText(
|
||||
App.getContext(),
|
||||
"Playlist already exists",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
if (songs.isNotEmpty()) {
|
||||
Toast.makeText(
|
||||
App.getContext(),
|
||||
"Adding songs to $playlistName",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,24 +22,26 @@ enum class NowPlayingScreen constructor(
|
|||
@param:StringRes @field:StringRes
|
||||
val titleRes: Int,
|
||||
@param:DrawableRes @field:DrawableRes val drawableResId: Int,
|
||||
val id: Int
|
||||
val id: Int,
|
||||
val defaultCoverTheme: AlbumCoverStyle?
|
||||
) {
|
||||
// Some Now playing themes look better with particular Album cover theme
|
||||
|
||||
Adaptive(R.string.adaptive, R.drawable.np_adaptive, 10),
|
||||
Blur(R.string.blur, R.drawable.np_blur, 4),
|
||||
BlurCard(R.string.blur_card, R.drawable.np_blur_card, 9),
|
||||
Card(R.string.card, R.drawable.np_card, 6),
|
||||
Circle(R.string.circle, R.drawable.np_minimalistic_circle, 15),
|
||||
Classic(R.string.classic, R.drawable.np_classic, 16),
|
||||
Color(R.string.color, R.drawable.np_color, 5),
|
||||
Fit(R.string.fit, R.drawable.np_fit, 12),
|
||||
Flat(R.string.flat, R.drawable.np_flat, 1),
|
||||
Full(R.string.full, R.drawable.np_full, 2),
|
||||
Gradient(R.string.gradient, R.drawable.np_gradient, 17),
|
||||
Material(R.string.material, R.drawable.np_material, 11),
|
||||
Normal(R.string.normal, R.drawable.np_normal, 0),
|
||||
Peak(R.string.peak, R.drawable.np_peak, 14),
|
||||
Plain(R.string.plain, R.drawable.np_plain, 3),
|
||||
Simple(R.string.simple, R.drawable.np_simple, 8),
|
||||
Tiny(R.string.tiny, R.drawable.np_tiny, 7),
|
||||
Adaptive(R.string.adaptive, R.drawable.np_adaptive, 10, AlbumCoverStyle.FullCard),
|
||||
Blur(R.string.blur, R.drawable.np_blur, 4, AlbumCoverStyle.Normal),
|
||||
BlurCard(R.string.blur_card, R.drawable.np_blur_card, 9, AlbumCoverStyle.Card),
|
||||
Card(R.string.card, R.drawable.np_card, 6, AlbumCoverStyle.Full),
|
||||
Circle(R.string.circle, R.drawable.np_minimalistic_circle, 15, null),
|
||||
Classic(R.string.classic, R.drawable.np_classic, 16, AlbumCoverStyle.Full),
|
||||
Color(R.string.color, R.drawable.np_color, 5, AlbumCoverStyle.Normal),
|
||||
Fit(R.string.fit, R.drawable.np_fit, 12, AlbumCoverStyle.Full),
|
||||
Flat(R.string.flat, R.drawable.np_flat, 1, AlbumCoverStyle.Flat),
|
||||
Full(R.string.full, R.drawable.np_full, 2, AlbumCoverStyle.Full),
|
||||
Gradient(R.string.gradient, R.drawable.np_gradient, 17, AlbumCoverStyle.Full),
|
||||
Material(R.string.material, R.drawable.np_material, 11, AlbumCoverStyle.Normal),
|
||||
Normal(R.string.normal, R.drawable.np_normal, 0, AlbumCoverStyle.Normal),
|
||||
Peak(R.string.peak, R.drawable.np_peak, 14, AlbumCoverStyle.Normal),
|
||||
Plain(R.string.plain, R.drawable.np_plain, 3, AlbumCoverStyle.Normal),
|
||||
Simple(R.string.simple, R.drawable.np_simple, 8, AlbumCoverStyle.Normal),
|
||||
Tiny(R.string.tiny, R.drawable.np_tiny, 7, null),
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import androidx.recyclerview.widget.GridLayoutManager
|
|||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
|
||||
import code.name.monkey.retromusic.extensions.navigate
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.GridStyle
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
|
|
|
@ -83,10 +83,6 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = FragmentArtistDetailsBinding.bind(view)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
|
|
|
@ -7,7 +7,7 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
|
@ -49,7 +49,9 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
|
|||
val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
it?.let {
|
||||
backupViewModel.restoreBackup(requireActivity(), requireContext().contentResolver.openInputStream(it))
|
||||
startActivity(Intent(context, RestoreActivity::class.java).apply {
|
||||
data = it
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,17 +105,11 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
|
|||
}
|
||||
|
||||
override fun onBackupClicked(file: File) {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.restore)
|
||||
.setMessage(R.string.restore_message)
|
||||
.setPositiveButton(R.string.restore) { _, _ ->
|
||||
lifecycleScope.launch {
|
||||
backupViewModel.restoreBackup(requireActivity(), file.inputStream())
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
.show()
|
||||
lifecycleScope.launch {
|
||||
startActivity(Intent(context, RestoreActivity::class.java).apply {
|
||||
data = file.toUri()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
|
|
|
@ -5,6 +5,8 @@ import android.content.Intent
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.helper.BackupContent
|
||||
import code.name.monkey.retromusic.helper.BackupHelper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -25,12 +27,12 @@ class BackupViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun restoreBackup(activity: Activity, inputStream: InputStream?) {
|
||||
BackupHelper.restoreBackup(activity, inputStream)
|
||||
suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List<BackupContent>) {
|
||||
BackupHelper.restoreBackup(activity, inputStream, contents)
|
||||
withContext(Dispatchers.Main) {
|
||||
val intent = Intent(
|
||||
activity,
|
||||
activity::class.java
|
||||
MainActivity::class.java
|
||||
)
|
||||
activity.startActivity(intent)
|
||||
exitProcess(0)
|
||||
|
|
|
@ -1,12 +1,89 @@
|
|||
package code.name.monkey.retromusic.fragments.backup
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.databinding.ActivityRestoreBinding
|
||||
import code.name.monkey.retromusic.helper.BackupContent
|
||||
import code.name.monkey.retromusic.helper.BackupContent.*
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.theme.ThemeManager
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class RestoreActivity : AppCompatActivity() {
|
||||
|
||||
lateinit var binding: ActivityRestoreBinding
|
||||
private val backupViewModel: BackupViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
updateTheme()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_restore)
|
||||
binding = ActivityRestoreBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
val backupUri = intent?.data
|
||||
binding.backupName.setText(getFileName(backupUri))
|
||||
binding.cancelButton.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
binding.restoreButton.setOnClickListener {
|
||||
val backupContents = mutableListOf<BackupContent>()
|
||||
if (binding.checkSettings.isChecked) backupContents.add(SETTINGS)
|
||||
if (binding.checkQueue.isChecked) backupContents.add(QUEUE)
|
||||
if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS)
|
||||
if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES)
|
||||
if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES)
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (backupUri != null) {
|
||||
contentResolver.openInputStream(backupUri)?.use {
|
||||
backupViewModel.restoreBackup(this@RestoreActivity, it, backupContents)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTheme() {
|
||||
AppCompatDelegate.setDefaultNightMode(ThemeManager.getNightMode(this))
|
||||
|
||||
// Apply dynamic colors to activity if enabled
|
||||
if (PreferenceUtil.materialYou) {
|
||||
DynamicColors.applyIfAvailable(
|
||||
this,
|
||||
com.google.android.material.R.style.ThemeOverlay_Material3_DynamicColors_DayNight
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileName(uri: Uri?): String? {
|
||||
when (uri?.scheme) {
|
||||
"file" -> {
|
||||
return uri.lastPathSegment
|
||||
}
|
||||
"content" -> {
|
||||
val proj = arrayOf(MediaStore.Images.Media.TITLE)
|
||||
contentResolver.query(
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
||||
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
||||
} else {
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||
}, proj, null, null, null
|
||||
)?.use { cursor ->
|
||||
if (cursor.count != 0) {
|
||||
val columnIndex: Int =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE)
|
||||
cursor.moveToFirst()
|
||||
return cursor.getString(columnIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Backup"
|
||||
}
|
||||
}
|
|
@ -17,13 +17,13 @@ package code.name.monkey.retromusic.fragments.base
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.extensions.setLightStatusBarAuto
|
||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||
|
||||
|
@ -52,7 +52,7 @@ abstract class AbsMainActivityFragment(@LayoutRes layout: Int) : AbsMusicService
|
|||
}
|
||||
|
||||
fun setStatusBarColorAuto(view: View) {
|
||||
val colorPrimary = ATHUtil.resolveColor(requireContext(), R.attr.colorSurface)
|
||||
val colorPrimary = surfaceColor()
|
||||
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
||||
if (VersionUtils.hasMarshmallow()) {
|
||||
setStatusBarColor(view, colorPrimary)
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.media.MediaMetadataRetriever
|
|||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.text.TextUtils
|
||||
import android.view.GestureDetector
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
|
@ -33,6 +32,7 @@ import android.view.View
|
|||
import android.widget.RelativeLayout
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.graphics.drawable.DrawableWrapper
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -46,18 +46,17 @@ import code.name.monkey.retromusic.activities.MainActivity
|
|||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.SongEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.dialogs.*
|
||||
import code.name.monkey.retromusic.extensions.currentFragment
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.whichFragment
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.IPaletteColorHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||
import code.name.monkey.retromusic.repository.RealRepository
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.*
|
||||
|
@ -67,7 +66,6 @@ import kotlinx.coroutines.Dispatchers.Main
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.android.ext.android.get
|
||||
import java.io.FileNotFoundException
|
||||
import kotlin.math.abs
|
||||
|
||||
abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout),
|
||||
|
@ -81,8 +79,12 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
val song = MusicPlayerRemote.currentSong
|
||||
when (item.itemId) {
|
||||
R.id.action_toggle_lyrics -> {
|
||||
PreferenceUtil.showLyrics = !PreferenceUtil.showLyrics
|
||||
showLyricsIcon(item)
|
||||
PreferenceUtil.showLyrics = !item.isChecked
|
||||
item.isChecked = !item.isChecked
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_lyrics -> {
|
||||
goToLyrics(requireActivity())
|
||||
return true
|
||||
}
|
||||
R.id.action_toggle_favorite -> {
|
||||
|
@ -191,18 +193,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
return false
|
||||
}
|
||||
|
||||
private fun showLyricsIcon(item: MenuItem) {
|
||||
val icon =
|
||||
if (PreferenceUtil.showLyrics) R.drawable.ic_lyrics else R.drawable.ic_lyrics_outline
|
||||
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
|
||||
requireContext(),
|
||||
icon,
|
||||
toolbarIconColor()
|
||||
)
|
||||
item.isChecked = PreferenceUtil.showLyrics
|
||||
item.icon = drawable
|
||||
}
|
||||
|
||||
abstract fun playerToolbar(): Toolbar?
|
||||
|
||||
abstract fun onShow()
|
||||
|
@ -219,7 +209,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
updateLyrics()
|
||||
}
|
||||
|
||||
override fun onFavoriteStateChanged() {
|
||||
|
@ -231,7 +220,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
|
||||
if (playlist != null) {
|
||||
val songEntity = song.toSongEntity(playlist.playListId)
|
||||
val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty()
|
||||
val isFavorite = libraryViewModel.isSongFavorite(song.id)
|
||||
if (isFavorite) {
|
||||
libraryViewModel.removeSongFromPlaylist(songEntity)
|
||||
} else {
|
||||
|
@ -245,32 +234,28 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
|
||||
fun updateIsFavorite(animate: Boolean = false) {
|
||||
lifecycleScope.launch(IO) {
|
||||
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
|
||||
if (playlist != null) {
|
||||
val song: SongEntity =
|
||||
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
|
||||
val isFavorite: Boolean = libraryViewModel.isFavoriteSong(song).isNotEmpty()
|
||||
withContext(Main) {
|
||||
val icon = if (animate) {
|
||||
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
|
||||
} else {
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
}
|
||||
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
|
||||
requireContext(),
|
||||
icon,
|
||||
toolbarIconColor()
|
||||
)
|
||||
if (playerToolbar() != null) {
|
||||
playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite)?.apply {
|
||||
setIcon(drawable)
|
||||
title =
|
||||
if (isFavorite) getString(R.string.action_remove_from_favorites)
|
||||
else getString(R.string.action_add_to_favorites)
|
||||
getIcon().also {
|
||||
if (it is AnimatedVectorDrawable) {
|
||||
it.start()
|
||||
}
|
||||
val isFavorite: Boolean =
|
||||
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
|
||||
withContext(Main) {
|
||||
val icon = if (animate) {
|
||||
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
|
||||
} else {
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
}
|
||||
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
|
||||
requireContext(),
|
||||
icon,
|
||||
toolbarIconColor()
|
||||
)
|
||||
if (playerToolbar() != null) {
|
||||
playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite)?.apply {
|
||||
setIcon(drawable)
|
||||
title =
|
||||
if (isFavorite) getString(R.string.action_remove_from_favorites)
|
||||
else getString(R.string.action_add_to_favorites)
|
||||
getIcon().also {
|
||||
if (it is AnimatedVectorDrawable) {
|
||||
it.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,32 +264,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateLyrics() {
|
||||
setLyrics(null)
|
||||
lifecycleScope.launch(IO) {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
val lyrics = try {
|
||||
var data: String? = LyricUtil.getStringFromFile(song.title, song.artistName)
|
||||
if (TextUtils.isEmpty(data)) {
|
||||
data = MusicUtil.getLyrics(song)
|
||||
if (TextUtils.isEmpty(data)) {
|
||||
null
|
||||
} else {
|
||||
Lyrics.parse(song, data)
|
||||
}
|
||||
} else Lyrics.parse(song, data!!)
|
||||
} catch (err: FileNotFoundException) {
|
||||
null
|
||||
}
|
||||
withContext(Main) {
|
||||
setLyrics(lyrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun setLyrics(l: Lyrics?) {
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
if (PreferenceUtil.isFullScreenMode &&
|
||||
|
@ -320,8 +279,19 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val nps = PreferenceUtil.nowPlayingScreen
|
||||
|
||||
if (nps == NowPlayingScreen.Circle || nps == NowPlayingScreen.Peak || nps == NowPlayingScreen.Tiny) {
|
||||
playerToolbar()?.menu?.removeItem(R.id.action_toggle_lyrics)
|
||||
} else {
|
||||
playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.apply {
|
||||
fixCheckStateOnIcon()
|
||||
isCheckable = true
|
||||
isChecked = PreferenceUtil.showLyrics
|
||||
}
|
||||
}
|
||||
requireView().setOnTouchListener(
|
||||
SwipeDetector(
|
||||
requireContext(),
|
||||
|
@ -329,7 +299,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
requireView()
|
||||
)
|
||||
)
|
||||
playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.let { showLyricsIcon(it) }
|
||||
}
|
||||
|
||||
class SwipeDetector(val context: Context, val viewPager: ViewPager?, val view: View) :
|
||||
|
@ -443,4 +412,15 @@ fun goToLyrics(activity: Activity) {
|
|||
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) }
|
||||
|
|
|
@ -34,6 +34,8 @@ import code.name.monkey.retromusic.extensions.accentColor
|
|||
import code.name.monkey.retromusic.extensions.dip
|
||||
import code.name.monkey.retromusic.extensions.drawNextToNavbar
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.IScrollHelper
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.ThemedFastScroller.create
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialFadeThrough
|
||||
|
@ -42,7 +44,7 @@ import me.zhanghai.android.fastscroll.FastScroller
|
|||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||
|
||||
abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
|
||||
AbsMainActivityFragment(R.layout.fragment_main_recycler) {
|
||||
AbsMainActivityFragment(R.layout.fragment_main_recycler), IScrollHelper {
|
||||
|
||||
private var _binding: FragmentMainRecyclerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
@ -56,15 +58,15 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
_binding = FragmentMainRecyclerBinding.bind(view)
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
enterTransition = MaterialFadeThrough().apply {
|
||||
addTarget(binding.recyclerView)
|
||||
}
|
||||
enterTransition = MaterialFadeThrough().addTarget(binding.recyclerView)
|
||||
reenterTransition = MaterialFadeThrough().addTarget(binding.recyclerView)
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
mainActivity.supportActionBar?.title = null
|
||||
initLayoutManager()
|
||||
initAdapter()
|
||||
setUpRecyclerView()
|
||||
setupToolbar()
|
||||
binding.shuffleButton.fitsSystemWindows = PreferenceUtil.isFullScreenMode
|
||||
// Add listeners when shuffle is visible
|
||||
if (isShuffleVisible) {
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
|
@ -200,7 +202,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
|
||||
val container get() = binding.root
|
||||
|
||||
fun scrollToTop() {
|
||||
override fun scrollToTop() {
|
||||
recyclerView.scrollToPosition(0)
|
||||
binding.appBarLayout.setExpanded(true, true)
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@ import android.os.Environment
|
|||
import android.text.Html
|
||||
import android.view.*
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.loader.app.LoaderManager
|
||||
import androidx.loader.content.Loader
|
||||
import androidx.navigation.Navigation.findNavController
|
||||
|
@ -31,7 +31,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
|
||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
|
@ -40,8 +39,7 @@ import code.name.monkey.retromusic.adapter.Storage
|
|||
import code.name.monkey.retromusic.adapter.StorageAdapter
|
||||
import code.name.monkey.retromusic.adapter.StorageClickListener
|
||||
import code.name.monkey.retromusic.databinding.FragmentFolderBinding
|
||||
import code.name.monkey.retromusic.extensions.drawNextToNavbar
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||
import code.name.monkey.retromusic.fragments.folder.FoldersFragment.ListPathsAsyncTask.OnPathsListedCallback
|
||||
import code.name.monkey.retromusic.fragments.folder.FoldersFragment.ListSongsAsyncTask.OnSongsListedCallback
|
||||
|
@ -107,9 +105,9 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
|
|||
mainActivity.addMusicServiceEventListener(libraryViewModel)
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
mainActivity.supportActionBar?.title = null
|
||||
enterTransition = MaterialFadeThrough().apply {
|
||||
addTarget(binding.recyclerView)
|
||||
}
|
||||
enterTransition = MaterialFadeThrough()
|
||||
reenterTransition = MaterialFadeThrough()
|
||||
|
||||
setUpBreadCrumbs()
|
||||
setUpRecyclerView()
|
||||
setUpAdapter()
|
||||
|
@ -188,7 +186,7 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
|
|||
}
|
||||
|
||||
override fun onFileMenuClicked(file: File, view: View) {
|
||||
val popupMenu = PopupMenu(activity, view)
|
||||
val popupMenu = PopupMenu(requireActivity(), view)
|
||||
if (file.isDirectory) {
|
||||
popupMenu.inflate(R.menu.menu_item_directory)
|
||||
popupMenu.setOnMenuItemClickListener { item: MenuItem ->
|
||||
|
@ -453,12 +451,10 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
|
|||
private fun checkForPadding() {
|
||||
val count = adapter?.itemCount ?: 0
|
||||
if (_binding != null) {
|
||||
val params = binding.root.layoutParams as ViewGroup.MarginLayoutParams
|
||||
params.bottomMargin = if (count > 0 && playingQueue.isNotEmpty()) dip2px(
|
||||
requireContext(),
|
||||
104f
|
||||
) else dip2px(requireContext(), 54f)
|
||||
binding.root.layoutParams = params
|
||||
binding.recyclerView.updatePadding(
|
||||
bottom = if (count > 0 && playingQueue.isNotEmpty()) dip(R.dimen.mini_player_height_expanded)
|
||||
else dip(R.dimen.mini_player_height_expanded)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,24 +522,20 @@ class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
|
|||
|
||||
private fun setUpBreadCrumbs() {
|
||||
binding.breadCrumbs.setActivatedContentColor(
|
||||
resolveColor(requireContext(), android.R.attr.textColorPrimary)
|
||||
textColorPrimary()
|
||||
)
|
||||
binding.breadCrumbs.setDeactivatedContentColor(
|
||||
resolveColor(requireContext(), android.R.attr.textColorSecondary)
|
||||
textColorSecondary()
|
||||
|
||||
)
|
||||
binding.breadCrumbs.setCallback(this)
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(
|
||||
activity
|
||||
)
|
||||
val fastScroller = create(
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||
create(
|
||||
binding.recyclerView
|
||||
)
|
||||
binding.recyclerView.setOnApplyWindowInsetsListener(
|
||||
ScrollingViewOnApplyWindowInsetsListener(binding.recyclerView, fastScroller)
|
||||
)
|
||||
}
|
||||
|
||||
private fun toList(file: File): ArrayList<File> {
|
||||
|
|
|
@ -27,12 +27,10 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import code.name.monkey.retromusic.EXTRA_GENRE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.GenreAdapter
|
||||
import code.name.monkey.retromusic.extensions.navigate
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.view.View
|
|||
import androidx.activity.addCallback
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
@ -37,9 +38,11 @@ import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
|
|||
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.drawNextToNavbar
|
||||
import code.name.monkey.retromusic.extensions.elevatedAccentColor
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.interfaces.IScrollHelper
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
|
@ -47,7 +50,8 @@ import com.google.android.material.transition.MaterialFadeThrough
|
|||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
||||
class HomeFragment :
|
||||
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) {
|
||||
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home),
|
||||
IScrollHelper {
|
||||
|
||||
private var _binding: HomeBindingAdapter? = null
|
||||
private val binding get() = _binding!!
|
||||
|
@ -60,9 +64,8 @@ class HomeFragment :
|
|||
setupListeners()
|
||||
binding.titleWelcome.text = String.format("%s", PreferenceUtil.userName)
|
||||
|
||||
enterTransition = MaterialFadeThrough().apply {
|
||||
addTarget(binding.contentContainer)
|
||||
}
|
||||
enterTransition = MaterialFadeThrough().addTarget(binding.contentContainer)
|
||||
reenterTransition = MaterialFadeThrough().addTarget(binding.contentContainer)
|
||||
|
||||
val homeAdapter = HomeAdapter(mainActivity)
|
||||
binding.recyclerView.apply {
|
||||
|
@ -75,6 +78,7 @@ class HomeFragment :
|
|||
|
||||
loadProfile()
|
||||
setupTitle()
|
||||
colorButtons()
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
|
@ -84,6 +88,21 @@ class HomeFragment :
|
|||
remove()
|
||||
mainActivity.finish()
|
||||
}
|
||||
view.doOnLayout {
|
||||
adjustPlaylistButtons()
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustPlaylistButtons() {
|
||||
val buttons =
|
||||
listOf(binding.history, binding.lastAdded, binding.topPlayed, binding.actionShuffle)
|
||||
buttons.maxOf { it.lineCount }.let { maxLineCount->
|
||||
buttons.forEach { button ->
|
||||
// Set the highest line count to every button for consistency
|
||||
button.setLines(maxLineCount)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
|
@ -172,6 +191,13 @@ class HomeFragment :
|
|||
.into(binding.userImage)
|
||||
}
|
||||
|
||||
fun colorButtons() {
|
||||
binding.history.elevatedAccentColor()
|
||||
binding.lastAdded.elevatedAccentColor()
|
||||
binding.topPlayed.elevatedAccentColor()
|
||||
binding.actionShuffle.elevatedAccentColor()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.menu_main, menu)
|
||||
|
@ -189,7 +215,7 @@ class HomeFragment :
|
|||
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
|
||||
}
|
||||
|
||||
fun scrollToTop() {
|
||||
override fun scrollToTop() {
|
||||
binding.container.scrollTo(0, 0)
|
||||
binding.appBarLayout.setExpanded(true)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
package code.name.monkey.retromusic.fragments.other
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.SHOW_LYRICS
|
||||
import code.name.monkey.retromusic.databinding.FragmentCoverLyricsBinding
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||
import code.name.monkey.retromusic.util.LyricUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class CoverLyricsFragment : AbsMusicServiceFragment(R.layout.fragment_cover_lyrics),
|
||||
MusicProgressViewUpdateHelper.Callback, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
|
||||
private var _binding: FragmentCoverLyricsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val lyricsLayout: FrameLayout get() = binding.playerLyrics
|
||||
private val lyricsLine1: TextView get() = binding.playerLyricsLine1
|
||||
private val lyricsLine2: TextView get() = binding.playerLyricsLine2
|
||||
|
||||
private var lyrics: Lyrics? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = FragmentCoverLyricsBinding.bind(view)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||
if (PreferenceUtil.showLyrics) {
|
||||
progressViewUpdateHelper?.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == SHOW_LYRICS) {
|
||||
if (sharedPreferences?.getBoolean(key, false) == true) {
|
||||
progressViewUpdateHelper?.start()
|
||||
binding.root.isVisible = true
|
||||
updateLyrics()
|
||||
} else {
|
||||
progressViewUpdateHelper?.stop()
|
||||
binding.root.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
if (PreferenceUtil.showLyrics) {
|
||||
updateLyrics()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
if (PreferenceUtil.showLyrics) {
|
||||
updateLyrics()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLyrics() {
|
||||
lyrics = null
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
lyrics = try {
|
||||
val lrcFile: File? = LyricUtil.getSyncedLyricsFile(song)
|
||||
val data: String = LyricUtil.getStringFromLrc(lrcFile)
|
||||
Lyrics.parse(song,
|
||||
if (!TextUtils.isEmpty(data)) {
|
||||
data
|
||||
} else {
|
||||
// Get Embedded Lyrics
|
||||
LyricUtil.getEmbeddedSyncedLyrics(song.data)
|
||||
}
|
||||
)
|
||||
} catch (err: FileNotFoundException) {
|
||||
null
|
||||
} catch (e: CannotReadException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
if (_binding == null) return
|
||||
|
||||
if (!isLyricsLayoutVisible()) {
|
||||
hideLyricsLayout()
|
||||
return
|
||||
}
|
||||
|
||||
if (lyrics !is AbsSynchronizedLyrics) return
|
||||
val synchronizedLyrics = lyrics as AbsSynchronizedLyrics
|
||||
|
||||
lyricsLayout.visibility = View.VISIBLE
|
||||
lyricsLayout.alpha = 1f
|
||||
|
||||
val oldLine = lyricsLine2.text.toString()
|
||||
val line = synchronizedLyrics.getLine(progress)
|
||||
|
||||
if (oldLine != line || oldLine.isEmpty()) {
|
||||
lyricsLine1.text = oldLine
|
||||
lyricsLine2.text = line
|
||||
|
||||
lyricsLine1.isVisible = true
|
||||
lyricsLine2.isVisible = true
|
||||
|
||||
lyricsLine2.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(
|
||||
lyricsLine2.measuredWidth,
|
||||
View.MeasureSpec.EXACTLY
|
||||
),
|
||||
View.MeasureSpec.UNSPECIFIED
|
||||
)
|
||||
val h: Float = lyricsLine2.measuredHeight.toFloat()
|
||||
|
||||
lyricsLine1.alpha = 1f
|
||||
lyricsLine1.translationY = 0f
|
||||
lyricsLine1.animate().alpha(0f).translationY(-h).duration =
|
||||
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
|
||||
|
||||
lyricsLine2.alpha = 0f
|
||||
lyricsLine2.translationY = h
|
||||
lyricsLine2.animate().alpha(1f).translationY(0f).duration =
|
||||
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLyricsLayoutVisible(): Boolean {
|
||||
return lyrics != null && lyrics!!.isSynchronized && lyrics!!.isValid
|
||||
}
|
||||
|
||||
private fun hideLyricsLayout() {
|
||||
lyricsLayout.animate().alpha(0f).setDuration(AbsPlayerFragment.VISIBILITY_ANIM_DURATION)
|
||||
.withEndAction {
|
||||
if (_binding == null) return@withEndAction
|
||||
lyricsLayout.isVisible = false
|
||||
lyricsLine1.text = null
|
||||
lyricsLine2.text = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.unregisterOnSharedPreferenceChangeListener(this)
|
||||
progressViewUpdateHelper?.stop()
|
||||
_binding = null
|
||||
}
|
||||
}
|
|
@ -64,14 +64,6 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
|||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
|
||||
}
|
||||
}
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
binding.progressIndicator.hide()
|
||||
when (args.type) {
|
||||
|
@ -92,6 +84,10 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
|||
binding.recyclerView.updatePadding(bottom = height.toInt())
|
||||
}
|
||||
})
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
}
|
||||
|
||||
private fun lastAddedSongs() {
|
||||
|
@ -104,6 +100,7 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
|||
binding.recyclerView.apply {
|
||||
adapter = songAdapter
|
||||
layoutManager = linearLayoutManager()
|
||||
scheduleLayoutAnimation()
|
||||
}
|
||||
libraryViewModel.recentSongs().observe(viewLifecycleOwner, { songs ->
|
||||
songAdapter.swapDataSet(songs)
|
||||
|
|
|
@ -231,13 +231,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
|
|||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun editSyncedLyrics() {
|
||||
var lrcFile: File? = null
|
||||
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
||||
lrcFile = LyricUtil.getLocalLyricOriginalFile(song.data)
|
||||
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
lrcFile = LyricUtil.getLocalLyricFile(song.title, song.artistName)
|
||||
}
|
||||
val content: String = LyricUtil.getStringFromLrc(lrcFile)
|
||||
val content: String = LyricUtil.getStringFromLrc(LyricUtil.getSyncedLyricsFile(song))
|
||||
|
||||
MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||
title(res = R.string.edit_synced_lyrics)
|
||||
|
@ -332,11 +326,8 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
|
|||
|
||||
fun loadLRCLyrics() {
|
||||
binding.lyricsView.setLabel("Empty")
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
||||
binding.lyricsView.loadLrc(LyricUtil.getLocalLyricOriginalFile(song.data))
|
||||
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
binding.lyricsView.loadLrc(LyricUtil.getLocalLyricFile(song.title, song.artistName))
|
||||
LyricUtil.getSyncedLyricsFile(MusicPlayerRemote.currentSong)?.let {
|
||||
binding.lyricsView.loadLrc(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ import code.name.monkey.retromusic.extensions.show
|
|||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
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.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
||||
|
@ -110,13 +112,13 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
|
|||
}
|
||||
|
||||
private fun updateSongCover() {
|
||||
// val song = MusicPlayerRemote.currentSong
|
||||
// GlideApp.with(requireContext())
|
||||
// .asBitmap()
|
||||
// .songCoverOptions(song)
|
||||
// .transition(RetroGlideExtension.getDefaultTransition())
|
||||
// .load(RetroGlideExtension.getSongModel(song))
|
||||
// .into(binding.image)
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
GlideApp.with(requireContext())
|
||||
.asBitmap()
|
||||
.songCoverOptions(song)
|
||||
.transition(RetroGlideExtension.getDefaultTransition())
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.into(binding.image)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
|
|
|
@ -16,7 +16,6 @@ package code.name.monkey.retromusic.fragments.other
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
|
@ -31,8 +30,6 @@ import androidx.core.view.doOnPreDraw
|
|||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.Constants.USER_BANNER
|
||||
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
||||
import code.name.monkey.retromusic.R
|
||||
|
@ -87,6 +84,7 @@ class UserInfoFragment : Fragment() {
|
|||
applyToolbar(binding.toolbar)
|
||||
|
||||
binding.nameContainer.accentColor()
|
||||
binding.next.accentColor()
|
||||
binding.name.setText(PreferenceUtil.userName)
|
||||
|
||||
binding.userImage.setOnClickListener {
|
||||
|
@ -111,14 +109,6 @@ class UserInfoFragment : Fragment() {
|
|||
findNavController().navigateUp()
|
||||
}
|
||||
|
||||
val textColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
ColorUtil.isColorLight(accentColor())
|
||||
)
|
||||
binding.next.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||
binding.next.iconTint = ColorStateList.valueOf(textColor)
|
||||
binding.next.setTextColor(textColor)
|
||||
loadProfile()
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw {
|
||||
|
@ -134,12 +124,11 @@ class UserInfoFragment : Fragment() {
|
|||
private fun loadProfile() {
|
||||
binding.bannerImage.let {
|
||||
GlideApp.with(this)
|
||||
.asBitmap()
|
||||
.load(RetroGlideExtension.getBannerModel())
|
||||
.profileBannerOptions(RetroGlideExtension.getBannerModel())
|
||||
.into(it)
|
||||
}
|
||||
GlideApp.with(this).asBitmap()
|
||||
GlideApp.with(this)
|
||||
.load(RetroGlideExtension.getUserModel())
|
||||
.userProfileOptions(RetroGlideExtension.getUserModel())
|
||||
.into(binding.userImage)
|
||||
|
@ -217,7 +206,7 @@ class UserInfoFragment : Fragment() {
|
|||
val appDir = requireContext().filesDir
|
||||
val file = File(appDir, fileName)
|
||||
var successful = false
|
||||
kotlin.runCatching {
|
||||
runCatching {
|
||||
val os = BufferedOutputStream(FileOutputStream(file))
|
||||
successful = ImageUtil.resizeBitmap(bitmap, 2048)
|
||||
.compress(Bitmap.CompressFormat.WEBP, 100, os)
|
||||
|
|
|
@ -14,28 +14,30 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.fragments.player
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.SHOW_LYRICS
|
||||
import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter
|
||||
import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter.AlbumCoverFragment
|
||||
import code.name.monkey.retromusic.databinding.FragmentPlayerAlbumCoverBinding
|
||||
import code.name.monkey.retromusic.extensions.isColorLight
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToLyrics
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
||||
import code.name.monkey.retromusic.lyrics.CoverLrcView
|
||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||
import code.name.monkey.retromusic.transform.CarousalPagerTransformer
|
||||
import code.name.monkey.retromusic.transform.ParallaxPagerTransformer
|
||||
|
@ -45,11 +47,6 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover),
|
||||
ViewPager.OnPageChangeListener, MusicProgressViewUpdateHelper.Callback,
|
||||
|
@ -70,9 +67,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
|||
}
|
||||
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
|
||||
|
||||
private val lyricsLayout: FrameLayout get() = binding.playerLyrics
|
||||
private val lyricsLine1: TextView get() = binding.playerLyricsLine1
|
||||
private val lyricsLine2: TextView get() = binding.playerLyricsLine2
|
||||
private val lrcView: CoverLrcView get() = binding.lyricsView
|
||||
|
||||
var lyrics: Lyrics? = null
|
||||
|
||||
|
@ -82,102 +77,33 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
|||
}
|
||||
|
||||
private fun updateLyrics() {
|
||||
lyrics = null
|
||||
binding.lyricsView.setLabel(context?.getString(R.string.no_lyrics_found))
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
val lyrics = try {
|
||||
var lrcFile: File? = null
|
||||
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
||||
lrcFile = LyricUtil.getLocalLyricOriginalFile(song.data)
|
||||
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
lrcFile = LyricUtil.getLocalLyricFile(song.title, song.artistName)
|
||||
val lrcFile = LyricUtil.getSyncedLyricsFile(song)
|
||||
if (lrcFile != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.lyricsView.loadLrc(lrcFile)
|
||||
}
|
||||
val data: String = LyricUtil.getStringFromLrc(lrcFile)
|
||||
if (!TextUtils.isEmpty(data)) {
|
||||
Lyrics.parse(song, data)
|
||||
} else {
|
||||
// Get Embedded Lyrics and check if they are Synchronized
|
||||
val embeddedLyrics = try{
|
||||
AudioFileIO.read(File(song.data)).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
|
||||
} catch(e: Exception){
|
||||
null
|
||||
}
|
||||
if (AbsSynchronizedLyrics.isSynchronized(embeddedLyrics)) {
|
||||
Lyrics.parse(song, embeddedLyrics)
|
||||
} else {
|
||||
val embeddedLyrics = LyricUtil.getEmbeddedSyncedLyrics(song.data)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (embeddedLyrics != null) {
|
||||
binding.lyricsView.loadLrc(embeddedLyrics)
|
||||
} else {
|
||||
null
|
||||
binding.lyricsView.reset()
|
||||
}
|
||||
}
|
||||
} catch (err: FileNotFoundException) {
|
||||
null
|
||||
} catch (e: CannotReadException){
|
||||
null
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
this@PlayerAlbumCoverFragment.lyrics = lyrics
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
if (_binding == null) return
|
||||
|
||||
if (!isLyricsLayoutVisible()) {
|
||||
hideLyricsLayout()
|
||||
return
|
||||
}
|
||||
|
||||
if (lyrics !is AbsSynchronizedLyrics) return
|
||||
val synchronizedLyrics = lyrics as AbsSynchronizedLyrics
|
||||
|
||||
lyricsLayout.visibility = View.VISIBLE
|
||||
lyricsLayout.alpha = 1f
|
||||
|
||||
val oldLine = lyricsLine2.text.toString()
|
||||
val line = synchronizedLyrics.getLine(progress)
|
||||
|
||||
if (oldLine != line || oldLine.isEmpty()) {
|
||||
lyricsLine1.text = oldLine
|
||||
lyricsLine2.text = line
|
||||
|
||||
lyricsLine1.visibility = View.VISIBLE
|
||||
lyricsLine2.visibility = View.VISIBLE
|
||||
|
||||
lyricsLine2.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(
|
||||
lyricsLine2.measuredWidth,
|
||||
View.MeasureSpec.EXACTLY
|
||||
),
|
||||
View.MeasureSpec.UNSPECIFIED
|
||||
)
|
||||
val h: Float = lyricsLine2.measuredHeight.toFloat()
|
||||
|
||||
lyricsLine1.alpha = 1f
|
||||
lyricsLine1.translationY = 0f
|
||||
lyricsLine1.animate().alpha(0f).translationY(-h).duration =
|
||||
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
|
||||
|
||||
lyricsLine2.alpha = 0f
|
||||
lyricsLine2.translationY = h
|
||||
lyricsLine2.animate().alpha(1f).translationY(0f).duration =
|
||||
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLyricsLayoutVisible(): Boolean {
|
||||
return lyrics != null && lyrics!!.isSynchronized && lyrics!!.isValid
|
||||
}
|
||||
|
||||
private fun hideLyricsLayout() {
|
||||
lyricsLayout.animate().alpha(0f).setDuration(AbsPlayerFragment.VISIBILITY_ANIM_DURATION)
|
||||
.withEndAction {
|
||||
if (_binding == null) return@withEndAction
|
||||
lyricsLayout.visibility = View.GONE
|
||||
lyricsLine1.text = null
|
||||
lyricsLine2.text = null
|
||||
}
|
||||
binding.lyricsView.updateTime(progress.toLong())
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = FragmentPlayerAlbumCoverBinding.bind(view)
|
||||
|
@ -208,27 +134,34 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
|||
)
|
||||
}
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||
// Don't show lyrics container for below conditions
|
||||
if (!(nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics)) {
|
||||
progressViewUpdateHelper?.start()
|
||||
}
|
||||
// Go to lyrics activity when clicked lyrics
|
||||
binding.playerLyricsLine2.setOnClickListener {
|
||||
goToLyrics(requireActivity())
|
||||
maybeInitLyrics()
|
||||
lrcView.apply {
|
||||
setDraggable(true) { time ->
|
||||
MusicPlayerRemote.seekTo(time.toInt())
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
true
|
||||
}
|
||||
setOnFlingXListener { velocityX ->
|
||||
when {
|
||||
velocityX < 0 -> {
|
||||
MusicPlayerRemote.playNextSong()
|
||||
true
|
||||
}
|
||||
velocityX > 0 -> {
|
||||
MusicPlayerRemote.playPreviousSong()
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val nps = PreferenceUtil.nowPlayingScreen
|
||||
// Don't show lyrics container for below conditions
|
||||
if (nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics) {
|
||||
lyricsLayout.isVisible = false
|
||||
progressViewUpdateHelper?.stop()
|
||||
} else {
|
||||
lyricsLayout.isVisible = true
|
||||
progressViewUpdateHelper?.start()
|
||||
}
|
||||
maybeInitLyrics()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
@ -259,25 +192,51 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
|||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
||||
if (key == SHOW_LYRICS) {
|
||||
if (sharedPreferences.getBoolean(key, false)) {
|
||||
progressViewUpdateHelper?.start()
|
||||
lyricsLayout.animate().alpha(1f).duration =
|
||||
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
|
||||
binding.playerLyrics.isVisible = true
|
||||
maybeInitLyrics()
|
||||
} else {
|
||||
showLyrics(false)
|
||||
progressViewUpdateHelper?.stop()
|
||||
lyricsLayout.animate().alpha(0f)
|
||||
.setDuration(AbsPlayerFragment.VISIBILITY_ANIM_DURATION)
|
||||
.withEndAction {
|
||||
if (_binding != null) {
|
||||
binding.playerLyrics.isVisible = false
|
||||
lyricsLine1.text = null
|
||||
lyricsLine2.text = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLRCViewColors(backgroundColor: Int) {
|
||||
val primaryColor = MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
backgroundColor.isColorLight
|
||||
)
|
||||
val secondaryColor = MaterialValueHelper.getSecondaryDisabledTextColor(
|
||||
requireContext(),
|
||||
backgroundColor.isColorLight
|
||||
)
|
||||
lrcView.apply {
|
||||
setCurrentColor(primaryColor)
|
||||
setTimeTextColor(primaryColor)
|
||||
setTimelineColor(primaryColor)
|
||||
setNormalColor(secondaryColor)
|
||||
setTimelineTextColor(primaryColor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLyrics(visible: Boolean) {
|
||||
lrcView.isVisible = visible
|
||||
viewPager.isInvisible = visible
|
||||
}
|
||||
|
||||
private fun maybeInitLyrics() {
|
||||
val nps = PreferenceUtil.nowPlayingScreen
|
||||
// Don't show lyrics container for below conditions
|
||||
if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) {
|
||||
showLyrics(true)
|
||||
progressViewUpdateHelper?.start()
|
||||
lrcView.animate().alpha(1f).duration =
|
||||
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
|
||||
} else {
|
||||
showLyrics(false)
|
||||
progressViewUpdateHelper?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayingQueue() {
|
||||
binding.viewPager.apply {
|
||||
adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue)
|
||||
|
@ -309,6 +268,19 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
|||
|
||||
private fun notifyColorChange(color: MediaNotificationProcessor) {
|
||||
callbacks?.onColorChanged(color)
|
||||
setLRCViewColors(
|
||||
when (PreferenceUtil.nowPlayingScreen) {
|
||||
Adaptive, Fit, Plain, Simple -> surfaceColor()
|
||||
Flat, Normal -> if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.backgroundColor
|
||||
} else {
|
||||
surfaceColor()
|
||||
}
|
||||
Color -> color.backgroundColor
|
||||
Blur -> Color.BLACK
|
||||
else -> color.backgroundColor
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun setCallbacks(listener: Callbacks) {
|
||||
|
@ -325,4 +297,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
|||
companion object {
|
||||
val TAG: String = PlayerAlbumCoverFragment::class.java.simpleName
|
||||
}
|
||||
|
||||
private val lyricViewNpsList =
|
||||
listOf(Blur, Classic, Color, Flat, Material, Normal, Plain, Simple)
|
||||
}
|
||||
|
|
|
@ -17,14 +17,10 @@ package code.name.monkey.retromusic.fragments.player.adaptive
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentAdaptivePlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -47,7 +43,7 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) {
|
|||
_binding = FragmentAdaptivePlayerBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
binding.root.drawAboveSystemBars()
|
||||
binding.playbackControlsFragment.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
@ -108,7 +104,7 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) {
|
|||
libraryViewModel.updateColor(color.primaryTextColor)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -130,7 +126,7 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) {
|
|||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
return colorControlNormal()
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
|
|
|
@ -16,6 +16,8 @@ package code.name.monkey.retromusic.fragments.player.blur
|
|||
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
|
@ -27,18 +29,18 @@ import code.name.monkey.retromusic.databinding.FragmentBlurBinding
|
|||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.*
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
|
||||
|
||||
class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private var lastRequest: GlideRequest<Drawable>? = null
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return binding.playerToolbar
|
||||
}
|
||||
|
@ -111,23 +113,20 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
|
|||
get() = lastColor
|
||||
|
||||
private fun updateBlur() {
|
||||
binding.colorBackground.clearColorFilter()
|
||||
GlideApp.with(requireActivity()).asBitmapPalette()
|
||||
.songCoverOptions(MusicPlayerRemote.currentSong)
|
||||
// https://github.com/bumptech/glide/issues/527#issuecomment-148840717
|
||||
GlideApp.with(this)
|
||||
.load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong))
|
||||
.dontAnimate()
|
||||
.simpleSongCoverOptions(MusicPlayerRemote.currentSong)
|
||||
.transform(
|
||||
BlurTransformation.Builder(requireContext())
|
||||
.blurRadius(blurAmount.toFloat())
|
||||
BlurTransformation.Builder(requireContext()).blurRadius(blurAmount.toFloat())
|
||||
.build()
|
||||
)
|
||||
.into(object : RetroMusicColoredTarget(binding.colorBackground) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
if (colors.backgroundColor == defaultFooterColor) {
|
||||
binding.colorBackground.setColorFilter(colors.backgroundColor)
|
||||
}
|
||||
}
|
||||
})
|
||||
).thumbnail(lastRequest)
|
||||
.error(GlideApp.with(this).load(ColorDrawable(Color.DKGRAY)).fitCenter())
|
||||
.also {
|
||||
lastRequest = it.clone()
|
||||
it.crossfadeListener()
|
||||
.into(binding.colorBackground)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
|
|
|
@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.player.cardblur
|
|||
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
|
@ -28,10 +29,7 @@ import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
|||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.*
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
|
||||
|
@ -50,6 +48,7 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
|
|||
|
||||
private var _binding: FragmentCardBlurPlayerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private var lastRequest: GlideRequest<Drawable>? = null
|
||||
|
||||
override fun onShow() {
|
||||
playbackControlsFragment.show()
|
||||
|
@ -136,22 +135,19 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
|
|||
}
|
||||
|
||||
private fun updateBlur() {
|
||||
binding.colorBackground.clearColorFilter()
|
||||
GlideApp.with(requireActivity()).asBitmapPalette()
|
||||
.songCoverOptions(MusicPlayerRemote.currentSong)
|
||||
// https://github.com/bumptech/glide/issues/527#issuecomment-148840717
|
||||
GlideApp.with(this)
|
||||
.load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong))
|
||||
.dontAnimate()
|
||||
.simpleSongCoverOptions(MusicPlayerRemote.currentSong)
|
||||
.transform(
|
||||
BlurTransformation.Builder(requireContext()).blurRadius(blurAmount.toFloat())
|
||||
.build()
|
||||
)
|
||||
.into(object : RetroMusicColoredTarget(binding.colorBackground) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
if (colors.backgroundColor == defaultFooterColor) {
|
||||
binding.colorBackground.setColorFilter(colors.backgroundColor)
|
||||
}
|
||||
}
|
||||
})
|
||||
.thumbnail(lastRequest).also {
|
||||
lastRequest = it.clone()
|
||||
it.crossfadeListener()
|
||||
.into(binding.colorBackground)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -18,19 +18,19 @@ import android.animation.ObjectAnimator
|
|||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.appthemehelper.util.*
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
|
@ -38,6 +38,7 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
|||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
import code.name.monkey.retromusic.fragments.base.goToArtist
|
||||
import code.name.monkey.retromusic.glide.*
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
|
||||
|
@ -47,10 +48,9 @@ import code.name.monkey.retromusic.util.MusicUtil
|
|||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import code.name.monkey.retromusic.views.SeekArc
|
||||
import code.name.monkey.retromusic.views.SeekArc.OnSeekArcChangeListener
|
||||
import code.name.monkey.retromusic.volume.AudioVolumeObserver
|
||||
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener
|
||||
import me.tankery.lib.circularseekbar.CircularSeekBar
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2020-01-06.
|
||||
|
@ -58,7 +58,7 @@ import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener
|
|||
|
||||
class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player), Callback,
|
||||
OnAudioVolumeChangedListener,
|
||||
OnSeekArcChangeListener {
|
||||
CircularSeekBar.OnCircularSeekBarChangeListener {
|
||||
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
private var audioVolumeObserver: AudioVolumeObserver? = null
|
||||
|
@ -69,6 +69,9 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
private var _binding: FragmentCirclePlayerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var rotateAnimator: ObjectAnimator? = null
|
||||
private var lastRequest: GlideRequest<Drawable>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
|
@ -103,7 +106,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
setOnMenuItemClickListener(this@CirclePlayerFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -116,11 +119,17 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
ThemeStore.accentColor(requireContext()),
|
||||
false
|
||||
)
|
||||
binding.volumeSeekBar.progressColor = accentColor()
|
||||
binding.volumeSeekBar.arcColor = ColorUtil.withAlpha(accentColor(), 0.25f)
|
||||
binding.volumeSeekBar.circleProgressColor = accentColor()
|
||||
binding.volumeSeekBar.circleColor = ColorUtil.withAlpha(accentColor(), 0.25f)
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpPlayerToolbar()
|
||||
binding.albumCoverOverlay.background = ColorDrawable(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
accentColor().isColorLight
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
|
@ -144,6 +153,17 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun setupRotateAnimation() {
|
||||
rotateAnimator = ObjectAnimator.ofFloat(binding.albumCover, View.ROTATION, 360F).apply {
|
||||
interpolator = LinearInterpolator()
|
||||
repeatCount = Animation.INFINITE
|
||||
duration = 10000
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
|
@ -153,11 +173,11 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this)
|
||||
|
||||
val audioManager = audioManager
|
||||
if (audioManager != null) {
|
||||
binding.volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
binding.volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
}
|
||||
binding.volumeSeekBar.setOnSeekArcChangeListener(this)
|
||||
binding.volumeSeekBar.max =
|
||||
audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat()
|
||||
binding.volumeSeekBar.progress =
|
||||
audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).toFloat()
|
||||
binding.volumeSeekBar.setOnSeekBarChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
@ -178,7 +198,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
override fun onBackPressed(): Boolean = false
|
||||
|
||||
override fun toolbarIconColor(): Int =
|
||||
ATHUtil.resolveColor(requireContext(), android.R.attr.colorControlNormal)
|
||||
colorControlNormal()
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = Color.BLACK
|
||||
|
@ -191,6 +211,11 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
if (rotateAnimator?.isStarted == true) rotateAnimator?.resume() else rotateAnimator?.start()
|
||||
} else {
|
||||
rotateAnimator?.pause()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
|
@ -202,6 +227,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
super.onServiceConnected()
|
||||
updateSong()
|
||||
updatePlayPauseDrawableState()
|
||||
setupRotateAnimation()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
|
@ -215,6 +241,16 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
} else {
|
||||
binding.songInfo.hide()
|
||||
}
|
||||
GlideApp.with(this)
|
||||
.load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong))
|
||||
.simpleSongCoverOptions(MusicPlayerRemote.currentSong)
|
||||
.thumbnail(lastRequest)
|
||||
.error(GlideApp.with(this).load(R.drawable.default_audio_art).fitCenter())
|
||||
.fitCenter().also {
|
||||
lastRequest = it.clone()
|
||||
it.crossfadeListener()
|
||||
.into(binding.albumCover)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
|
@ -225,8 +261,8 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
}
|
||||
|
||||
override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) {
|
||||
_binding?.volumeSeekBar?.max = maxVolume
|
||||
_binding?.volumeSeekBar?.progress = currentVolume
|
||||
_binding?.volumeSeekBar?.max = maxVolume.toFloat()
|
||||
_binding?.volumeSeekBar?.progress = currentVolume.toFloat()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -237,15 +273,16 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
_binding = null
|
||||
}
|
||||
|
||||
override fun onProgressChanged(seekArc: SeekArc?, progress: Int, fromUser: Boolean) {
|
||||
|
||||
override fun onProgressChanged(seekBar: CircularSeekBar?, progress: Float, fromUser: Boolean) {
|
||||
val audioManager = audioManager
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0)
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress.toInt(), 0)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekArc: SeekArc?) {
|
||||
override fun onStartTrackingTouch(seekBar: CircularSeekBar?) {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekArc: SeekArc?) {
|
||||
override fun onStopTrackingTouch(seekBar: CircularSeekBar?) {
|
||||
}
|
||||
|
||||
fun setUpProgressSlider() {
|
||||
|
|
|
@ -28,7 +28,6 @@ import androidx.appcompat.widget.Toolbar
|
|||
import androidx.core.view.ViewCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
|
@ -39,6 +38,7 @@ import code.name.monkey.retromusic.databinding.FragmentClassicPlayerBinding
|
|||
import code.name.monkey.retromusic.extensions.getSongInfo
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
|
@ -144,7 +144,7 @@ class ClassicPlayerFragment : AbsPlayerFragment(R.layout.fragment_classic_player
|
|||
).build()
|
||||
)
|
||||
shapeDrawable.fillColor =
|
||||
ColorStateList.valueOf(ATHUtil.resolveColor(requireContext(), R.attr.colorSurface))
|
||||
ColorStateList.valueOf(surfaceColor())
|
||||
binding.playerQueueSheet.background = shapeDrawable
|
||||
|
||||
binding.playerQueueSheet.setOnTouchListener { _, _ ->
|
||||
|
|
|
@ -19,10 +19,10 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.animation.doOnEnd
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentColorPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
|
@ -127,7 +127,7 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
|
|||
setOnMenuItemClickListener(this@ColorFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ package code.name.monkey.retromusic.fragments.player.fit
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentFitBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
|
@ -56,7 +56,7 @@ class FitFragment : AbsPlayerFragment(R.layout.fragment_fit) {
|
|||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
return colorControlNormal()
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
|
@ -65,7 +65,7 @@ class FitFragment : AbsPlayerFragment(R.layout.fragment_fit) {
|
|||
libraryViewModel.updateColor(color.primaryTextColor)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ class FitFragment : AbsPlayerFragment(R.layout.fragment_fit) {
|
|||
setOnMenuItemClickListener(this@FitFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -23,16 +23,12 @@ import android.view.animation.DecelerateInterpolator
|
|||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentFitPlaybackControlsBinding
|
||||
import code.name.monkey.retromusic.extensions.getSongInfo
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.ripAlpha
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
import code.name.monkey.retromusic.fragments.base.goToArtist
|
||||
|
@ -132,8 +128,7 @@ class FitPlaybackControlsFragment :
|
|||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
|
||||
if (ColorUtil.isColorLight(colorBg)) {
|
||||
if (ColorUtil.isColorLight(colorControlNormal())) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
|
|
|
@ -20,12 +20,12 @@ import android.graphics.drawable.GradientDrawable
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentFlatPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
|
@ -65,7 +65,7 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) {
|
|||
binding.playerToolbar.setOnMenuItemClickListener(this)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) {
|
|||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(animation.animatedValue as Int, android.R.color.transparent), 0
|
||||
)
|
||||
binding.colorGradientBackground.background = drawable
|
||||
_binding?.colorGradientBackground?.background = drawable
|
||||
}
|
||||
valueAnimator?.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong())?.start()
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) {
|
|||
_binding = FragmentFlatPlayerBinding.bind(view)
|
||||
setUpPlayerToolbar()
|
||||
setUpSubFragments()
|
||||
binding.playbackControlsFragment.drawAboveSystemBars()
|
||||
binding.playerToolbar.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
|
@ -112,21 +112,16 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) {
|
|||
return if (PreferenceUtil.isAdaptiveColor)
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), isLight)
|
||||
else
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
colorControlNormal()
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
controlsFragment.setColor(color)
|
||||
libraryViewModel.updateColor(color.backgroundColor)
|
||||
val isLight = ColorUtil.isColorLight(color.backgroundColor)
|
||||
val iconColor = if (PreferenceUtil.isAdaptiveColor)
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), isLight)
|
||||
else
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
iconColor,
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
if (PreferenceUtil.isAdaptiveColor) {
|
||||
|
|
|
@ -26,14 +26,13 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.SongEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.extensions.applyColor
|
||||
import code.name.monkey.retromusic.extensions.getSongInfo
|
||||
|
@ -205,6 +204,7 @@ class FullPlaybackControlsFragment :
|
|||
popupMenu.setOnMenuItemClickListener(this)
|
||||
popupMenu.inflate(R.menu.menu_player)
|
||||
popupMenu.menu.findItem(R.id.action_toggle_favorite).isVisible = false
|
||||
popupMenu.menu.findItem(R.id.action_toggle_lyrics).isChecked = PreferenceUtil.showLyrics
|
||||
popupMenu.show()
|
||||
}
|
||||
}
|
||||
|
@ -317,28 +317,24 @@ class FullPlaybackControlsFragment :
|
|||
|
||||
fun updateIsFavorite(animate: Boolean = false) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
|
||||
if (playlist != null) {
|
||||
val song: SongEntity =
|
||||
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
|
||||
val isFavorite: Boolean = libraryViewModel.isFavoriteSong(song).isNotEmpty()
|
||||
withContext(Dispatchers.Main) {
|
||||
val icon = if (animate) {
|
||||
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
|
||||
} else {
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
}
|
||||
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
|
||||
requireContext(),
|
||||
icon,
|
||||
Color.WHITE
|
||||
)
|
||||
binding.songFavourite.apply {
|
||||
setImageDrawable(drawable)
|
||||
getDrawable().also {
|
||||
if (it is AnimatedVectorDrawable) {
|
||||
it.start()
|
||||
}
|
||||
val isFavorite: Boolean =
|
||||
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
val icon = if (animate) {
|
||||
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
|
||||
} else {
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
}
|
||||
val drawable: Drawable = RetroUtil.getTintedVectorDrawable(
|
||||
requireContext(),
|
||||
icon,
|
||||
Color.WHITE
|
||||
)
|
||||
binding.songFavourite.apply {
|
||||
setImageDrawable(drawable)
|
||||
getDrawable().also {
|
||||
if (it is AnimatedVectorDrawable) {
|
||||
it.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ import android.graphics.drawable.AnimatedVectorDrawable
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
|
@ -39,9 +39,6 @@ import code.name.monkey.retromusic.R
|
|||
import code.name.monkey.retromusic.RetroBottomSheetBehavior
|
||||
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
||||
import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.SongEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
|
@ -81,7 +78,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
private var bottomInsets = 0
|
||||
private var navBarHeight = 0
|
||||
|
||||
private var _binding: FragmentGradientPlayerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
@ -92,8 +89,8 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
binding.playerQueueSheet.updatePadding(
|
||||
top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt()
|
||||
)
|
||||
binding.container.updatePadding(
|
||||
bottom = ((1 - slideOffset) * bottomInsets).toInt()
|
||||
binding.recyclerView.updatePadding(
|
||||
top = ((1 - slideOffset) * navBarHeight).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -126,6 +123,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
popupMenu.setOnMenuItemClickListener(this)
|
||||
popupMenu.inflate(R.menu.menu_player)
|
||||
popupMenu.menu.findItem(R.id.action_toggle_favorite).isVisible = false
|
||||
popupMenu.menu.findItem(R.id.action_toggle_lyrics).isChecked = PreferenceUtil.showLyrics
|
||||
popupMenu.show()
|
||||
}
|
||||
}
|
||||
|
@ -160,9 +158,9 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
}
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
(binding.container)
|
||||
) { v: View, insets: WindowInsetsCompat ->
|
||||
bottomInsets = insets.safeGetBottomInsets()
|
||||
v.updatePadding(bottom = bottomInsets)
|
||||
) { _: View, insets: WindowInsetsCompat ->
|
||||
navBarHeight = insets.safeGetBottomInsets()
|
||||
binding.recyclerView.updatePadding(top = navBarHeight)
|
||||
insets
|
||||
}
|
||||
binding.playbackControlsFragment.root.drawAboveSystemBars()
|
||||
|
@ -281,23 +279,19 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
|
||||
private fun updateIsFavoriteIcon(animate: Boolean = false) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist()
|
||||
if (playlist != null) {
|
||||
val song: SongEntity =
|
||||
MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
|
||||
val isFavorite: Boolean = libraryViewModel.isFavoriteSong(song).isNotEmpty()
|
||||
withContext(Dispatchers.Main) {
|
||||
val icon = if (animate) {
|
||||
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
|
||||
} else {
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
}
|
||||
binding.playbackControlsFragment.songFavourite.apply {
|
||||
setImageResource(icon)
|
||||
drawable.also {
|
||||
if (it is AnimatedVectorDrawable) {
|
||||
it.start()
|
||||
}
|
||||
val isFavorite: Boolean =
|
||||
libraryViewModel.isSongFavorite(MusicPlayerRemote.currentSong.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
val icon = if (animate) {
|
||||
if (isFavorite) R.drawable.avd_favorite else R.drawable.avd_unfavorite
|
||||
} else {
|
||||
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
|
||||
}
|
||||
binding.playbackControlsFragment.songFavourite.apply {
|
||||
setImageResource(icon)
|
||||
drawable.also {
|
||||
if (it is AnimatedVectorDrawable) {
|
||||
it.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +473,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
oldBottom: Int
|
||||
) {
|
||||
val panel = getQueuePanel()
|
||||
panel.peekHeight = binding.container.height
|
||||
panel.peekHeight = binding.container.height + navBarHeight
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
|
|
|
@ -18,10 +18,10 @@ import android.graphics.Color
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentHomePlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
|
@ -128,7 +128,7 @@ class HomePlayerFragment : AbsPlayerFragment(R.layout.fragment_home_player),
|
|||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ package code.name.monkey.retromusic.fragments.player.material
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentMaterialBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
|
@ -62,9 +62,7 @@ class MaterialFragment : AbsPlayerFragment(R.layout.fragment_material) {
|
|||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
override fun toolbarIconColor() = colorControlNormal()
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
playbackControlsFragment.setColor(color)
|
||||
|
@ -73,7 +71,7 @@ class MaterialFragment : AbsPlayerFragment(R.layout.fragment_material) {
|
|||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -112,7 +110,7 @@ class MaterialFragment : AbsPlayerFragment(R.layout.fragment_material) {
|
|||
setOnMenuItemClickListener(this@MaterialFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(context, R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,15 +16,21 @@ package code.name.monkey.retromusic.fragments.player.normal
|
|||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.preference.PreferenceManager
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.SNOWFALL
|
||||
import code.name.monkey.retromusic.databinding.FragmentPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -34,7 +40,8 @@ import code.name.monkey.retromusic.util.ViewUtil
|
|||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import code.name.monkey.retromusic.views.DrawableGradient
|
||||
|
||||
class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
||||
class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
|
@ -54,7 +61,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
|||
|
||||
valueAnimator = ValueAnimator.ofObject(
|
||||
ArgbEvaluator(),
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorSurface),
|
||||
surfaceColor(),
|
||||
i
|
||||
)
|
||||
valueAnimator?.addUpdateListener { animation ->
|
||||
|
@ -63,7 +70,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
|||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(
|
||||
animation.animatedValue as Int,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorSurface)
|
||||
surfaceColor()
|
||||
), 0
|
||||
)
|
||||
binding.colorGradientBackground.background = drawable
|
||||
|
@ -85,9 +92,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
|||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
override fun toolbarIconColor() = colorControlNormal()
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
controlsFragment.setColor(color)
|
||||
|
@ -96,7 +101,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
|||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
|
||||
|
@ -121,11 +126,23 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
|||
_binding = FragmentPlayerBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
if (PreferenceUtil.isSnowFalling) {
|
||||
binding.snowfallView.isVisible = true
|
||||
binding.snowfallView.restartFalling()
|
||||
} else {
|
||||
binding.snowfallView.isVisible = false
|
||||
binding.snowfallView.stopFalling()
|
||||
}
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.registerOnSharedPreferenceChangeListener(this)
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.unregisterOnSharedPreferenceChangeListener(this)
|
||||
_binding = null
|
||||
}
|
||||
|
||||
|
@ -145,11 +162,21 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
|||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == SNOWFALL && PreferenceUtil.isSnowFalling) {
|
||||
binding.snowfallView.isVisible = true
|
||||
binding.snowfallView.restartFalling()
|
||||
} else {
|
||||
binding.snowfallView.isVisible = false
|
||||
binding.snowfallView.stopFalling()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
|
|
@ -17,14 +17,10 @@ package code.name.monkey.retromusic.fragments.player.peak
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentPeakPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.getSongInfo
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
import code.name.monkey.retromusic.fragments.base.goToArtist
|
||||
|
@ -57,7 +53,7 @@ class PeakPlayerFragment : AbsPlayerFragment(R.layout.fragment_peak_player) {
|
|||
binding.text.setOnClickListener {
|
||||
goToArtist(requireActivity())
|
||||
}
|
||||
binding.root.drawAboveSystemBars()
|
||||
binding.root.drawAboveSystemBarsWithPadding()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
@ -76,7 +72,7 @@ class PeakPlayerFragment : AbsPlayerFragment(R.layout.fragment_peak_player) {
|
|||
setOnMenuItemClickListener(this@PeakPlayerFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(context, R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -96,9 +92,7 @@ class PeakPlayerFragment : AbsPlayerFragment(R.layout.fragment_peak_player) {
|
|||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
override fun toolbarIconColor() = colorControlNormal()
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
|
|
@ -17,10 +17,10 @@ package code.name.monkey.retromusic.fragments.player.plain
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentPlainPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
|
@ -66,7 +66,7 @@ class PlainPlayerFragment : AbsPlayerFragment(R.layout.fragment_plain_player) {
|
|||
setOnMenuItemClickListener(this@PlainPlayerFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -109,9 +109,7 @@ class PlainPlayerFragment : AbsPlayerFragment(R.layout.fragment_plain_player) {
|
|||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
override fun toolbarIconColor() = colorControlNormal()
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
plainPlaybackControlsFragment.setColor(color)
|
||||
|
@ -119,7 +117,7 @@ class PlainPlayerFragment : AbsPlayerFragment(R.layout.fragment_plain_player) {
|
|||
libraryViewModel.updateColor(color.primaryTextColor)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ package code.name.monkey.retromusic.fragments.player.simple
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentSimplePlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
|
@ -75,9 +75,7 @@ class SimplePlayerFragment : AbsPlayerFragment(R.layout.fragment_simple_player)
|
|||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
override fun toolbarIconColor() = colorControlNormal()
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
|
@ -85,7 +83,7 @@ class SimplePlayerFragment : AbsPlayerFragment(R.layout.fragment_simple_player)
|
|||
controlsFragment.setColor(color)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
@ -107,7 +105,7 @@ class SimplePlayerFragment : AbsPlayerFragment(R.layout.fragment_simple_player)
|
|||
binding.playerToolbar.setOnMenuItemClickListener(this)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
colorControlNormal(),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -281,12 +281,13 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player),
|
|||
return gestureDetector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
@Suppress("Deprecation")
|
||||
private fun vibrate() {
|
||||
val v = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
v!!.vibrate(VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE))
|
||||
v?.vibrate(VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE))
|
||||
} else {
|
||||
v!!.vibrate(10)
|
||||
v?.vibrate(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class PlaylistsFragment :
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
libraryViewModel.getPlaylists().observe(viewLifecycleOwner, {
|
||||
if (it.isNotEmpty())
|
||||
adapter?.swapDataSet(it.filter { playlistWithSongs-> playlistWithSongs.songs.isNotEmpty() })
|
||||
adapter?.swapDataSet(it)
|
||||
else
|
||||
adapter?.swapDataSet(listOf())
|
||||
})
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.google.android.material.chip.ChipGroup
|
|||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import net.yslibrary.android.keyboardvisibilityevent.KeyboardVisibilityEvent
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
@ -70,7 +71,10 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
|
|||
setupRecyclerView()
|
||||
|
||||
binding.voiceSearch.setOnClickListener { startMicSearch() }
|
||||
binding.clearText.setOnClickListener { binding.searchView.clearText() }
|
||||
binding.clearText.setOnClickListener {
|
||||
binding.searchView.clearText()
|
||||
searchAdapter.swapDataSet(listOf())
|
||||
}
|
||||
binding.searchView.apply {
|
||||
addTextChangedListener(this@SearchFragment)
|
||||
focusAndShowKeyboard()
|
||||
|
@ -97,6 +101,13 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
|
|||
bottomMargin = it
|
||||
}
|
||||
})
|
||||
KeyboardVisibilityEvent.setEventListener(requireActivity(), viewLifecycleOwner) {
|
||||
if (it) {
|
||||
binding.keyboardPopup.isGone = true
|
||||
} else {
|
||||
binding.keyboardPopup.show()
|
||||
}
|
||||
}
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
}
|
||||
|
|
|
@ -16,16 +16,19 @@ package code.name.monkey.retromusic.fragments.settings
|
|||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat
|
||||
import code.name.monkey.retromusic.activities.OnThemeChangedListener
|
||||
import code.name.monkey.retromusic.extensions.rootView
|
||||
import code.name.monkey.retromusic.extensions.safeGetBottomInsets
|
||||
import code.name.monkey.retromusic.preferences.*
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
|
@ -67,14 +70,23 @@ abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setDivider(ColorDrawable(Color.TRANSPARENT))
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
listView.overScrollMode = View.OVER_SCROLL_NEVER
|
||||
}
|
||||
|
||||
// CollapsingToolbarLayout consumes insets and insets are not passed to child views
|
||||
// So we get insets from root view
|
||||
// So we get insets from decor view
|
||||
// https://github.com/material-components/material-components-android/issues/1310
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
view
|
||||
) { _, insets ->
|
||||
listView.updatePadding(bottom = insets.safeGetBottomInsets())
|
||||
insets
|
||||
requireActivity().rootView
|
||||
) { _, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
listView.updatePadding(
|
||||
left = insets.left,
|
||||
bottom = insets.bottom,
|
||||
right = insets.right,
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
invalidateSettings()
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
@ -26,11 +28,10 @@ import code.name.monkey.appthemehelper.ThemeStore
|
|||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentMainSettingsBinding
|
||||
import code.name.monkey.retromusic.extensions.addBottomInsets
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.rootView
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
class MainSettingsFragment : Fragment(), View.OnClickListener {
|
||||
|
||||
|
@ -87,8 +88,17 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
|
|||
binding.buyPremium.setTextColor(it)
|
||||
binding.diamondIcon.imageTintList = ColorStateList.valueOf(it)
|
||||
}
|
||||
if (!RetroUtil.isLandscape()) {
|
||||
binding.container.updatePadding(bottom = RetroUtil.getNavigationBarHeight())
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
requireActivity().rootView
|
||||
) { _, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
_binding?.container?.updatePadding(
|
||||
left = insets.left,
|
||||
bottom = insets.bottom,
|
||||
right = insets.right,
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,15 @@ import com.bumptech.glide.RequestBuilder
|
|||
import com.bumptech.glide.annotation.GlideExtension
|
||||
import com.bumptech.glide.annotation.GlideOption
|
||||
import com.bumptech.glide.annotation.GlideType
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.Key
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.BaseRequestOptions
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.DrawableCrossFadeFactory
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.bumptech.glide.signature.MediaStoreSignature
|
||||
import java.io.File
|
||||
|
||||
|
@ -116,6 +122,16 @@ object RetroGlideExtension {
|
|||
.signature(createSignature(song))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun simpleSongCoverOptions(
|
||||
baseRequestOptions: BaseRequestOptions<*>,
|
||||
song: Song
|
||||
): BaseRequestOptions<*> {
|
||||
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.signature(createSignature(song))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun albumCoverOptions(
|
||||
|
@ -194,4 +210,33 @@ object RetroGlideExtension {
|
|||
fun <TranscodeType> getDefaultTransition(): GenericTransitionOptions<TranscodeType> {
|
||||
return GenericTransitionOptions<TranscodeType>().transition(DEFAULT_ANIMATION)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/bumptech/glide/issues/527#issuecomment-148840717
|
||||
fun GlideRequest<Drawable>.crossfadeListener(): GlideRequest<Drawable> {
|
||||
return listener(object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>?,
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
return if (isFirstResource) {
|
||||
false // thumbnail was not shown, do as usual
|
||||
} else DrawableCrossFadeFactory.Builder()
|
||||
.setCrossFadeEnabled(true).build()
|
||||
.build(dataSource, isFirstResource)
|
||||
.transition(resource, target as Transition.ViewAdapter)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -16,9 +16,8 @@ package code.name.monkey.retromusic.glide
|
|||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.ImageView
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.colorControlNormal
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
|
@ -27,7 +26,7 @@ import com.bumptech.glide.request.transition.Transition
|
|||
abstract class RetroMusicColoredTarget(view: ImageView) : BitmapPaletteTarget(view) {
|
||||
|
||||
protected val defaultFooterColor: Int
|
||||
get() = ATHUtil.resolveColor(getView().context, R.attr.colorControlNormal)
|
||||
get() = getView().context.colorControlNormal()
|
||||
|
||||
abstract fun onColorReady(colors: MediaNotificationProcessor)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
package code.name.monkey.retromusic.glide.audiocover;
|
||||
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.audio.mp3.MP3File;
|
||||
|
@ -45,7 +46,7 @@ public class AudioFileCoverUtils {
|
|||
}
|
||||
}
|
||||
// If there are any exceptions, we ignore them and continue to the other fallback method
|
||||
} catch (ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException ignored) {
|
||||
} catch (ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException | CannotReadException ignored) {
|
||||
}
|
||||
|
||||
// Method 2: look for album art in external files
|
||||
|
|
|
@ -2,9 +2,13 @@ package code.name.monkey.retromusic.helper
|
|||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.helper.BackupContent.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.*
|
||||
|
@ -24,10 +28,11 @@ object BackupHelper {
|
|||
zipItems.addAll(getSettingsZipItems(context))
|
||||
getUserImageZipItems(context)?.let { zipItems.addAll(it) }
|
||||
zipItems.addAll(getCustomArtistZipItems(context))
|
||||
zipItems.addAll(getQueueZipItems(context))
|
||||
zipAll(zipItems, backupFile)
|
||||
}
|
||||
|
||||
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) {
|
||||
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) =
|
||||
withContext(Dispatchers.IO) {
|
||||
kotlin.runCatching {
|
||||
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
|
||||
|
@ -42,27 +47,42 @@ object BackupHelper {
|
|||
}
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
throw Exception(it)
|
||||
}.onSuccess {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
App.getContext(),
|
||||
"Backup created successfully",
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(App.getContext(), "Backup created successfully", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDatabaseZipItems(context: Context): List<ZipItem> {
|
||||
return context.databaseList().filter {
|
||||
it.endsWith(".db")
|
||||
it.endsWith(".db") && it != queueDatabase
|
||||
}.map {
|
||||
ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getQueueZipItems(context: Context): List<ZipItem> {
|
||||
Log.d("RetroMusic", context.getDatabasePath(queueDatabase).absolutePath)
|
||||
return listOf(
|
||||
ZipItem(
|
||||
context.getDatabasePath(queueDatabase).absolutePath,
|
||||
"$QUEUE_PATH${File.separator}$queueDatabase"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getSettingsZipItems(context: Context): List<ZipItem> {
|
||||
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/"
|
||||
return listOf(
|
||||
|
@ -94,33 +114,47 @@ object BackupHelper {
|
|||
)
|
||||
}?.toList() ?: listOf()
|
||||
)
|
||||
zipItemList.add(
|
||||
ZipItem(
|
||||
sharedPrefPath + File.separator + "custom_artist_image.xml",
|
||||
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml"
|
||||
)
|
||||
)
|
||||
File(sharedPrefPath + File.separator + "custom_artist_image.xml").let {
|
||||
if (it.exists()) {
|
||||
zipItemList.add(
|
||||
ZipItem(
|
||||
it.absolutePath,
|
||||
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return zipItemList
|
||||
}
|
||||
|
||||
suspend fun restoreBackup(context: Context, inputStream: InputStream?) {
|
||||
suspend fun restoreBackup(
|
||||
context: Context,
|
||||
inputStream: InputStream?,
|
||||
contents: List<BackupContent>
|
||||
) {
|
||||
withContext(Dispatchers.IO) {
|
||||
ZipInputStream(inputStream).use {
|
||||
var entry = it.nextEntry
|
||||
while (entry != null) {
|
||||
if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry)
|
||||
if (entry.isPreferenceEntry()) restorePreferences(context, it, entry)
|
||||
if (entry.isImageEntry()) restoreImages(context, it, entry)
|
||||
if (entry.isCustomArtistImageEntry()) restoreCustomArtistImages(
|
||||
context,
|
||||
it,
|
||||
entry
|
||||
)
|
||||
if (entry.isCustomArtistPrefEntry()) restoreCustomArtistPrefs(
|
||||
context,
|
||||
it,
|
||||
entry
|
||||
)
|
||||
if (entry.isDatabaseEntry() && contents.contains(PLAYLISTS)) {
|
||||
restoreDatabase(context, it, entry)
|
||||
} else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) {
|
||||
restorePreferences(context, it, entry)
|
||||
} else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) {
|
||||
restoreImages(context, it, entry)
|
||||
|
||||
} else if (entry.isCustomArtistImageEntry() && contents.contains(
|
||||
CUSTOM_ARTIST_IMAGES
|
||||
)
|
||||
) {
|
||||
restoreCustomArtistImages(context, it, entry)
|
||||
restoreCustomArtistPrefs(context, it, entry)
|
||||
} else if (entry.isQueueEntry() && contents.contains(QUEUE)) {
|
||||
restoreQueueDatabase(context, it, entry)
|
||||
}
|
||||
|
||||
entry = it.nextEntry
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +204,21 @@ object BackupHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private fun restoreQueueDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) {
|
||||
putInt("POSITION", 0)
|
||||
}
|
||||
val filePath =
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreCustomArtistImages(
|
||||
context: Context,
|
||||
zipIn: ZipInputStream,
|
||||
|
@ -218,10 +267,12 @@ object BackupHelper {
|
|||
const val BACKUP_EXTENSION = "rmbak"
|
||||
const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
|
||||
private const val DATABASES_PATH = "databases"
|
||||
private const val QUEUE_PATH = "queue"
|
||||
private const val SETTINGS_PATH = "prefs"
|
||||
private const val IMAGES_PATH = "userImages"
|
||||
private const val CUSTOM_ARTISTS_PATH = "artistImages"
|
||||
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
|
||||
private const val queueDatabase = "music_playback_state.db"
|
||||
|
||||
private fun ZipEntry.isDatabaseEntry(): Boolean {
|
||||
return name.startsWith(DATABASES_PATH)
|
||||
|
@ -243,6 +294,10 @@ object BackupHelper {
|
|||
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
|
||||
}
|
||||
|
||||
private fun ZipEntry.isQueueEntry(): Boolean {
|
||||
return name.startsWith(QUEUE_PATH)
|
||||
}
|
||||
|
||||
private fun ZipEntry.getFileName(): String {
|
||||
return name.substring(name.lastIndexOf(File.separator))
|
||||
}
|
||||
|
@ -261,4 +316,12 @@ fun CharSequence.sanitize(): String {
|
|||
.replace("|", "_")
|
||||
.replace("\\", "_")
|
||||
.replace("&", "_")
|
||||
}
|
||||
|
||||
enum class BackupContent {
|
||||
SETTINGS,
|
||||
USER_IMAGES,
|
||||
CUSTOM_ARTIST_IMAGES,
|
||||
PLAYLISTS,
|
||||
QUEUE
|
||||
}
|
|
@ -31,15 +31,15 @@ object M3UWriter : M3UConstants {
|
|||
val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION)
|
||||
val songs = playlist.getSongs()
|
||||
if (songs.isNotEmpty()) {
|
||||
val bw = BufferedWriter(FileWriter(file))
|
||||
bw.write(M3UConstants.HEADER)
|
||||
for (song in songs) {
|
||||
bw.newLine()
|
||||
bw.write(M3UConstants.ENTRY + song.duration + M3UConstants.DURATION_SEPARATOR + song.artistName + " - " + song.title)
|
||||
bw.newLine()
|
||||
bw.write(song.data)
|
||||
BufferedWriter(FileWriter(file)).use { bw ->
|
||||
bw.write(M3UConstants.HEADER)
|
||||
for (song in songs) {
|
||||
bw.newLine()
|
||||
bw.write(M3UConstants.ENTRY + song.duration + M3UConstants.DURATION_SEPARATOR + song.artistName + " - " + song.title)
|
||||
bw.newLine()
|
||||
bw.write(song.data)
|
||||
}
|
||||
}
|
||||
bw.close()
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
@ -54,15 +54,15 @@ object M3UWriter : M3UConstants {
|
|||
it.songPrimaryKey
|
||||
}.toSongs()
|
||||
if (songs.isNotEmpty()) {
|
||||
val bufferedWriter = BufferedWriter(FileWriter(file))
|
||||
bufferedWriter.write(M3UConstants.HEADER)
|
||||
songs.forEach {
|
||||
bufferedWriter.newLine()
|
||||
bufferedWriter.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title)
|
||||
bufferedWriter.newLine()
|
||||
bufferedWriter.write(it.data)
|
||||
BufferedWriter(FileWriter(file)).use { bw->
|
||||
bw.write(M3UConstants.HEADER)
|
||||
songs.forEach {
|
||||
bw.newLine()
|
||||
bw.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title)
|
||||
bw.newLine()
|
||||
bw.write(it.data)
|
||||
}
|
||||
}
|
||||
bufferedWriter.close()
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
@ -72,15 +72,15 @@ object M3UWriter : M3UConstants {
|
|||
it.songPrimaryKey
|
||||
}.toSongs()
|
||||
if (songs.isNotEmpty()) {
|
||||
val bufferedWriter = outputStream.bufferedWriter()
|
||||
bufferedWriter.write(M3UConstants.HEADER)
|
||||
songs.forEach {
|
||||
bufferedWriter.newLine()
|
||||
bufferedWriter.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title)
|
||||
bufferedWriter.newLine()
|
||||
bufferedWriter.write(it.data)
|
||||
outputStream.bufferedWriter().use{ bw->
|
||||
bw.write(M3UConstants.HEADER)
|
||||
songs.forEach {
|
||||
bw.newLine()
|
||||
bw.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title)
|
||||
bw.newLine()
|
||||
bw.write(it.data)
|
||||
}
|
||||
}
|
||||
bufferedWriter.close()
|
||||
}
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
|
|
|
@ -17,7 +17,7 @@ package code.name.monkey.retromusic.helper.menu
|
|||
import android.content.Intent
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.navigation.findNavController
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package code.name.monkey.retromusic.interfaces
|
||||
|
||||
interface IScrollHelper {
|
||||
fun scrollToTop()
|
||||
}
|
|
@ -0,0 +1,719 @@
|
|||
/*
|
||||
* Copyright (C) 2017 wangchenyan
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the
|
||||
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
package code.name.monkey.retromusic.lyrics
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.AsyncTask
|
||||
import android.os.Looper
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import android.text.TextUtils
|
||||
import android.text.format.DateUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.GestureDetector.SimpleOnGestureListener
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.Scroller
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.retromusic.R
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* 歌词 Created by wcy on 2015/11/9.
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
class CoverLrcView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
private val mLrcEntryList: MutableList<LrcEntry> = ArrayList()
|
||||
private val mLrcPaint = TextPaint()
|
||||
private val mTimePaint = TextPaint()
|
||||
private var mTimeFontMetrics: Paint.FontMetrics? = null
|
||||
private var mPlayDrawable: Drawable? = null
|
||||
private var mDividerHeight = 0f
|
||||
private var mAnimationDuration: Long = 0
|
||||
private var mNormalTextColor = 0
|
||||
private var mNormalTextSize = 0f
|
||||
private var mCurrentTextColor = 0
|
||||
private var mCurrentTextSize = 0f
|
||||
private var mTimelineTextColor = 0
|
||||
private var mTimelineColor = 0
|
||||
private var mTimeTextColor = 0
|
||||
private var mDrawableWidth = 0
|
||||
private var mTimeTextWidth = 0
|
||||
private var mDefaultLabel: String? = null
|
||||
private var mLrcPadding = 0f
|
||||
private var mOnPlayClickListener: OnPlayClickListener? = null
|
||||
private var mOnFlingXListener: OnFlingXListener? = null
|
||||
private var mAnimator: ValueAnimator? = null
|
||||
private var mGestureDetector: GestureDetector? = null
|
||||
private var mScroller: Scroller? = null
|
||||
private var mOffset = 0f
|
||||
private var mCurrentLine = 0
|
||||
private var flag: Any? = null
|
||||
private var isShowTimeline = false
|
||||
private var isTouching = false
|
||||
private var isFling = false
|
||||
private var mTextGravity // 歌词显示位置,靠左/居中/靠右
|
||||
= 0
|
||||
private val hideTimelineRunnable = Runnable {
|
||||
if (hasLrc() && isShowTimeline) {
|
||||
isShowTimeline = false
|
||||
smoothScrollTo(mCurrentLine)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手势监听器
|
||||
*/
|
||||
private val mSimpleOnGestureListener: SimpleOnGestureListener =
|
||||
object : SimpleOnGestureListener() {
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
if (hasLrc() && mOnPlayClickListener != null) {
|
||||
if (mOffset != getOffset(0)) {
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
mScroller!!.forceFinished(true)
|
||||
removeCallbacks(hideTimelineRunnable)
|
||||
isTouching = true
|
||||
isShowTimeline = true
|
||||
invalidate()
|
||||
return true
|
||||
}
|
||||
return super.onDown(e)
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
): Boolean {
|
||||
if (mOffset == getOffset(0) && distanceY < 0F) {
|
||||
return super.onScroll(e1, e2, distanceX, distanceY)
|
||||
}
|
||||
if (hasLrc()) {
|
||||
mOffset += -distanceY
|
||||
mOffset = mOffset.coerceAtMost(getOffset(0))
|
||||
mOffset = mOffset.coerceAtLeast(getOffset(mLrcEntryList.size - 1))
|
||||
invalidate()
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
return true
|
||||
}
|
||||
return super.onScroll(e1, e2, distanceX, distanceY)
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
if (mOnFlingXListener != null && abs(velocityX) > abs(velocityY)) {
|
||||
return mOnFlingXListener!!.onFlingX(velocityX)
|
||||
}
|
||||
if (hasLrc()) {
|
||||
mScroller!!.fling(
|
||||
0,
|
||||
mOffset.toInt(),
|
||||
0,
|
||||
velocityY.toInt(),
|
||||
0,
|
||||
0,
|
||||
getOffset(mLrcEntryList.size - 1).toInt(),
|
||||
getOffset(0).toInt()
|
||||
)
|
||||
isFling = true
|
||||
return true
|
||||
}
|
||||
return super.onFling(e1, e2, velocityX, velocityY)
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
if (hasLrc()
|
||||
&& isShowTimeline
|
||||
&& mPlayDrawable!!.bounds.contains(e.x.toInt(), e.y.toInt())
|
||||
) {
|
||||
val centerLine = centerLine
|
||||
val centerLineTime = mLrcEntryList[centerLine].time
|
||||
// onPlayClick 消费了才更新 UI
|
||||
if (mOnPlayClickListener != null && mOnPlayClickListener!!.onPlayClick(
|
||||
centerLineTime
|
||||
)
|
||||
) {
|
||||
isShowTimeline = false
|
||||
removeCallbacks(hideTimelineRunnable)
|
||||
mCurrentLine = centerLine
|
||||
invalidate()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onSingleTapConfirmed(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun init(attrs: AttributeSet?) {
|
||||
val ta = context.obtainStyledAttributes(attrs, R.styleable.LrcView)
|
||||
mCurrentTextSize = ta.getDimension(
|
||||
R.styleable.LrcView_lrcTextSize, resources.getDimension(R.dimen.lrc_text_size)
|
||||
)
|
||||
mNormalTextSize = ta.getDimension(
|
||||
R.styleable.LrcView_lrcNormalTextSize,
|
||||
resources.getDimension(R.dimen.lrc_text_size)
|
||||
)
|
||||
if (mNormalTextSize == 0f) {
|
||||
mNormalTextSize = mCurrentTextSize
|
||||
}
|
||||
mDividerHeight = ta.getDimension(
|
||||
R.styleable.LrcView_lrcDividerHeight,
|
||||
resources.getDimension(R.dimen.lrc_divider_height)
|
||||
)
|
||||
val defDuration = resources.getInteger(R.integer.lrc_animation_duration)
|
||||
mAnimationDuration =
|
||||
ta.getInt(R.styleable.LrcView_lrcAnimationDuration, defDuration).toLong()
|
||||
mAnimationDuration =
|
||||
if (mAnimationDuration < 0) defDuration.toLong() else mAnimationDuration
|
||||
mNormalTextColor = ta.getColor(
|
||||
R.styleable.LrcView_lrcNormalTextColor,
|
||||
ContextCompat.getColor(context, R.color.lrc_normal_text_color)
|
||||
)
|
||||
mCurrentTextColor = ta.getColor(
|
||||
R.styleable.LrcView_lrcCurrentTextColor,
|
||||
ContextCompat.getColor(context, R.color.lrc_current_text_color)
|
||||
)
|
||||
mTimelineTextColor = ta.getColor(
|
||||
R.styleable.LrcView_lrcTimelineTextColor,
|
||||
ContextCompat.getColor(context, R.color.lrc_timeline_text_color)
|
||||
)
|
||||
mDefaultLabel = ta.getString(R.styleable.LrcView_lrcLabel)
|
||||
mDefaultLabel =
|
||||
if (TextUtils.isEmpty(mDefaultLabel)) context.getString(R.string.empty) else mDefaultLabel
|
||||
mLrcPadding = ta.getDimension(R.styleable.LrcView_lrcPadding, 0f)
|
||||
mTimelineColor = ta.getColor(
|
||||
R.styleable.LrcView_lrcTimelineColor,
|
||||
ContextCompat.getColor(context, R.color.lrc_timeline_color)
|
||||
)
|
||||
val timelineHeight = ta.getDimension(
|
||||
R.styleable.LrcView_lrcTimelineHeight,
|
||||
resources.getDimension(R.dimen.lrc_timeline_height)
|
||||
)
|
||||
mPlayDrawable = ta.getDrawable(R.styleable.LrcView_lrcPlayDrawable)
|
||||
mPlayDrawable =
|
||||
if (mPlayDrawable == null) ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.ic_play_arrow
|
||||
) else mPlayDrawable
|
||||
mTimeTextColor = ta.getColor(
|
||||
R.styleable.LrcView_lrcTimeTextColor,
|
||||
ContextCompat.getColor(context, R.color.lrc_time_text_color)
|
||||
)
|
||||
val timeTextSize = ta.getDimension(
|
||||
R.styleable.LrcView_lrcTimeTextSize,
|
||||
resources.getDimension(R.dimen.lrc_time_text_size)
|
||||
)
|
||||
mTextGravity = ta.getInteger(R.styleable.LrcView_lrcTextGravity, LrcEntry.GRAVITY_CENTER)
|
||||
ta.recycle()
|
||||
mDrawableWidth = resources.getDimension(R.dimen.lrc_drawable_width).toInt()
|
||||
mTimeTextWidth = resources.getDimension(R.dimen.lrc_time_width).toInt()
|
||||
mLrcPaint.isAntiAlias = true
|
||||
mLrcPaint.textSize = mCurrentTextSize
|
||||
mLrcPaint.textAlign = Paint.Align.LEFT
|
||||
mTimePaint.isAntiAlias = true
|
||||
mTimePaint.textSize = timeTextSize
|
||||
mTimePaint.textAlign = Paint.Align.CENTER
|
||||
mTimePaint.strokeWidth = timelineHeight
|
||||
mTimePaint.strokeCap = Paint.Cap.ROUND
|
||||
mTimeFontMetrics = mTimePaint.fontMetrics
|
||||
mGestureDetector = GestureDetector(context, mSimpleOnGestureListener)
|
||||
mGestureDetector!!.setIsLongpressEnabled(false)
|
||||
mScroller = Scroller(context)
|
||||
}
|
||||
|
||||
/** 设置非当前行歌词字体颜色 */
|
||||
fun setNormalColor(normalColor: Int) {
|
||||
mNormalTextColor = normalColor
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
/** 普通歌词文本字体大小 */
|
||||
fun setNormalTextSize(size: Float) {
|
||||
mNormalTextSize = size
|
||||
}
|
||||
|
||||
/** 当前歌词文本字体大小 */
|
||||
fun setCurrentTextSize(size: Float) {
|
||||
mCurrentTextSize = size
|
||||
}
|
||||
|
||||
/** 设置当前行歌词的字体颜色 */
|
||||
fun setCurrentColor(currentColor: Int) {
|
||||
mCurrentTextColor = currentColor
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
/** 设置拖动歌词时选中歌词的字体颜色 */
|
||||
fun setTimelineTextColor(timelineTextColor: Int) {
|
||||
mTimelineTextColor = timelineTextColor
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
/** 设置拖动歌词时时间线的颜色 */
|
||||
fun setTimelineColor(timelineColor: Int) {
|
||||
mTimelineColor = timelineColor
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
/** 设置拖动歌词时右侧时间字体颜色 */
|
||||
fun setTimeTextColor(timeTextColor: Int) {
|
||||
mTimeTextColor = timeTextColor
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置歌词是否允许拖动
|
||||
*
|
||||
* @param draggable 是否允许拖动
|
||||
* @param onPlayClickListener 设置歌词拖动后播放按钮点击监听器,如果允许拖动,则不能为 null
|
||||
*/
|
||||
fun setDraggable(draggable: Boolean, onPlayClickListener: OnPlayClickListener?) {
|
||||
mOnPlayClickListener = if (draggable) {
|
||||
requireNotNull(onPlayClickListener) { "if draggable == true, onPlayClickListener must not be null" }
|
||||
onPlayClickListener
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置播放按钮点击监听器
|
||||
*
|
||||
* @param onPlayClickListener 如果为非 null ,则激活歌词拖动功能,否则将将禁用歌词拖动功能
|
||||
*/
|
||||
@Deprecated("use {@link #setDraggable(boolean, OnPlayClickListener)} instead")
|
||||
fun setOnPlayClickListener(onPlayClickListener: OnPlayClickListener?) {
|
||||
mOnPlayClickListener = onPlayClickListener
|
||||
}
|
||||
|
||||
fun setOnFlingXListener(onFlingXListener: OnFlingXListener) {
|
||||
mOnFlingXListener = onFlingXListener
|
||||
}
|
||||
|
||||
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
|
||||
fun setLabel(label: String?) {
|
||||
runOnUi {
|
||||
mDefaultLabel = label
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载歌词文件
|
||||
*
|
||||
* @param lrcFile 歌词文件
|
||||
*/
|
||||
fun loadLrc(lrcFile: File) {
|
||||
loadLrc(lrcFile, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载双语歌词文件,两种语言的歌词时间戳需要一致
|
||||
*
|
||||
* @param mainLrcFile 第一种语言歌词文件
|
||||
* @param secondLrcFile 第二种语言歌词文件
|
||||
*/
|
||||
private fun loadLrc(mainLrcFile: File, secondLrcFile: File?) {
|
||||
runOnUi {
|
||||
reset()
|
||||
val sb = StringBuilder("file://")
|
||||
sb.append(mainLrcFile.path)
|
||||
if (secondLrcFile != null) {
|
||||
sb.append("#").append(secondLrcFile.path)
|
||||
}
|
||||
val flag = sb.toString()
|
||||
this.flag = flag
|
||||
object : AsyncTask<File?, Int?, List<LrcEntry>>() {
|
||||
override fun doInBackground(vararg params: File?): List<LrcEntry>? {
|
||||
return LrcUtils.parseLrc(params)
|
||||
}
|
||||
|
||||
override fun onPostExecute(lrcEntries: List<LrcEntry>) {
|
||||
if (flag == flag) {
|
||||
onLrcLoaded(lrcEntries)
|
||||
this@CoverLrcView.flag = null
|
||||
}
|
||||
}
|
||||
}.execute(mainLrcFile, secondLrcFile)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载歌词文本
|
||||
*
|
||||
* @param lrcText 歌词文本
|
||||
*/
|
||||
fun loadLrc(lrcText: String?) {
|
||||
loadLrc(lrcText, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载双语歌词文本,两种语言的歌词时间戳需要一致
|
||||
*
|
||||
* @param mainLrcText 第一种语言歌词文本
|
||||
* @param secondLrcText 第二种语言歌词文本
|
||||
*/
|
||||
private fun loadLrc(mainLrcText: String?, secondLrcText: String?) {
|
||||
runOnUi {
|
||||
reset()
|
||||
val sb = StringBuilder("file://")
|
||||
sb.append(mainLrcText)
|
||||
if (secondLrcText != null) {
|
||||
sb.append("#").append(secondLrcText)
|
||||
}
|
||||
val flag = sb.toString()
|
||||
this.flag = flag
|
||||
object : AsyncTask<String?, Int?, List<LrcEntry>>() {
|
||||
override fun doInBackground(vararg params: String?): List<LrcEntry>? {
|
||||
return LrcUtils.parseLrc(params)
|
||||
}
|
||||
|
||||
override fun onPostExecute(lrcEntries: List<LrcEntry>) {
|
||||
if (flag == flag) {
|
||||
onLrcLoaded(lrcEntries)
|
||||
this@CoverLrcView.flag = null
|
||||
}
|
||||
}
|
||||
}.execute(mainLrcText, secondLrcText)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载在线歌词,默认使用 utf-8 编码
|
||||
*
|
||||
* @param lrcUrl 歌词文件的网络地址
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun loadLrcByUrl(lrcUrl: String, charset: String? = "utf-8") {
|
||||
val flag = "url://$lrcUrl"
|
||||
this.flag = flag
|
||||
object : AsyncTask<String?, Int?, String>() {
|
||||
override fun doInBackground(vararg params: String?): String? {
|
||||
return LrcUtils.getContentFromNetwork(params[0], params[1])
|
||||
}
|
||||
|
||||
override fun onPostExecute(lrcText: String) {
|
||||
if (flag == flag) {
|
||||
loadLrc(lrcText)
|
||||
}
|
||||
}
|
||||
}.execute(lrcUrl, charset)
|
||||
}
|
||||
|
||||
/**
|
||||
* 歌词是否有效
|
||||
*
|
||||
* @return true,如果歌词有效,否则false
|
||||
*/
|
||||
fun hasLrc(): Boolean {
|
||||
return mLrcEntryList.isNotEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新歌词
|
||||
*
|
||||
* @param time 当前播放时间
|
||||
*/
|
||||
fun updateTime(time: Long) {
|
||||
runOnUi {
|
||||
if (!hasLrc()) {
|
||||
return@runOnUi
|
||||
}
|
||||
val line = findShowLine(time)
|
||||
if (line != mCurrentLine) {
|
||||
mCurrentLine = line
|
||||
if (!isShowTimeline) {
|
||||
smoothScrollTo(line)
|
||||
} else {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
if (changed) {
|
||||
initPlayDrawable()
|
||||
initEntryList()
|
||||
if (hasLrc()) {
|
||||
smoothScrollTo(mCurrentLine, 0L)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
val centerY = height / 2
|
||||
|
||||
// 无歌词文件
|
||||
if (!hasLrc()) {
|
||||
mLrcPaint.color = mCurrentTextColor
|
||||
@SuppressLint("DrawAllocation") val staticLayout = StaticLayout(
|
||||
mDefaultLabel,
|
||||
mLrcPaint,
|
||||
lrcWidth.toInt(),
|
||||
Layout.Alignment.ALIGN_CENTER,
|
||||
1f,
|
||||
0f,
|
||||
false
|
||||
)
|
||||
drawText(canvas, staticLayout, centerY.toFloat())
|
||||
return
|
||||
}
|
||||
val centerLine = centerLine
|
||||
if (isShowTimeline) {
|
||||
mPlayDrawable?.draw(canvas)
|
||||
mTimePaint.color = mTimeTextColor
|
||||
val timeText = LrcUtils.formatTime(mLrcEntryList[centerLine].time)
|
||||
val timeX = (width - mTimeTextWidth / 2).toFloat()
|
||||
val timeY = centerY - (mTimeFontMetrics!!.descent + mTimeFontMetrics!!.ascent) / 2
|
||||
canvas.drawText(timeText, timeX, timeY, mTimePaint)
|
||||
}
|
||||
canvas.translate(0f, mOffset)
|
||||
var y = 0f
|
||||
for (i in mLrcEntryList.indices) {
|
||||
if (i > 0) {
|
||||
y += ((mLrcEntryList[i - 1].height + mLrcEntryList[i].height shr 1)
|
||||
+ mDividerHeight)
|
||||
}
|
||||
if (i == mCurrentLine) {
|
||||
mLrcPaint.textSize = mCurrentTextSize
|
||||
mLrcPaint.color = mCurrentTextColor
|
||||
} else if (isShowTimeline && i == centerLine) {
|
||||
mLrcPaint.color = mTimelineTextColor
|
||||
} else {
|
||||
mLrcPaint.textSize = mNormalTextSize
|
||||
mLrcPaint.color = mNormalTextColor
|
||||
}
|
||||
drawText(canvas, mLrcEntryList[i].staticLayout, y)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 画一行歌词
|
||||
*
|
||||
* @param y 歌词中心 Y 坐标
|
||||
*/
|
||||
private fun drawText(canvas: Canvas, staticLayout: StaticLayout, y: Float) {
|
||||
canvas.save()
|
||||
canvas.translate(mLrcPadding, y - (staticLayout.height shr 1))
|
||||
staticLayout.draw(canvas)
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (event.action == MotionEvent.ACTION_UP
|
||||
|| event.action == MotionEvent.ACTION_CANCEL
|
||||
) {
|
||||
isTouching = false
|
||||
if (hasLrc() && !isFling) {
|
||||
adjustCenter()
|
||||
postDelayed(hideTimelineRunnable, TIMELINE_KEEP_TIME)
|
||||
}
|
||||
}
|
||||
return mGestureDetector!!.onTouchEvent(event)
|
||||
}
|
||||
|
||||
override fun computeScroll() {
|
||||
if (mScroller!!.computeScrollOffset()) {
|
||||
mOffset = mScroller!!.currY.toFloat()
|
||||
invalidate()
|
||||
}
|
||||
if (isFling && mScroller!!.isFinished) {
|
||||
isFling = false
|
||||
if (hasLrc() && !isTouching) {
|
||||
adjustCenter()
|
||||
postDelayed(hideTimelineRunnable, TIMELINE_KEEP_TIME)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
removeCallbacks(hideTimelineRunnable)
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
private fun onLrcLoaded(entryList: List<LrcEntry>?) {
|
||||
if (entryList != null && entryList.isNotEmpty()) {
|
||||
mLrcEntryList.addAll(entryList)
|
||||
}
|
||||
mLrcEntryList.sort()
|
||||
initEntryList()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun initPlayDrawable() {
|
||||
val l = (mTimeTextWidth - mDrawableWidth) / 2
|
||||
val t = height / 2 - mDrawableWidth / 2
|
||||
val r = l + mDrawableWidth
|
||||
val b = t + mDrawableWidth
|
||||
mPlayDrawable!!.setBounds(l, t, r, b)
|
||||
}
|
||||
|
||||
private fun initEntryList() {
|
||||
if (!hasLrc() || width == 0) {
|
||||
return
|
||||
}
|
||||
for (lrcEntry in mLrcEntryList) {
|
||||
lrcEntry.init(mLrcPaint, lrcWidth.toInt(), mTextGravity)
|
||||
}
|
||||
mOffset = (height / 2).toFloat()
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
endAnimation()
|
||||
mScroller!!.forceFinished(true)
|
||||
isShowTimeline = false
|
||||
isTouching = false
|
||||
isFling = false
|
||||
removeCallbacks(hideTimelineRunnable)
|
||||
mLrcEntryList.clear()
|
||||
mOffset = 0f
|
||||
mCurrentLine = 0
|
||||
invalidate()
|
||||
}
|
||||
|
||||
/** 将中心行微调至正中心 */
|
||||
private fun adjustCenter() {
|
||||
smoothScrollTo(centerLine, ADJUST_DURATION)
|
||||
}
|
||||
/** 滚动到某一行 */
|
||||
/** 滚动到某一行 */
|
||||
private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) {
|
||||
val offset = getOffset(line)
|
||||
endAnimation()
|
||||
mAnimator = ValueAnimator.ofFloat(mOffset, offset).apply {
|
||||
this.duration = duration
|
||||
interpolator = LinearInterpolator()
|
||||
addUpdateListener { animation: ValueAnimator ->
|
||||
mOffset = animation.animatedValue as Float
|
||||
invalidate()
|
||||
}
|
||||
LrcUtils.resetDurationScale()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
/** 结束滚动动画 */
|
||||
private fun endAnimation() {
|
||||
if (mAnimator != null && mAnimator!!.isRunning) {
|
||||
mAnimator!!.end()
|
||||
}
|
||||
}
|
||||
|
||||
/** 二分法查找当前时间应该显示的行数(最后一个 <= time 的行数) */
|
||||
private fun findShowLine(time: Long): Int {
|
||||
var left = 0
|
||||
var right = mLrcEntryList.size
|
||||
while (left <= right) {
|
||||
val middle = (left + right) / 2
|
||||
val middleTime = mLrcEntryList[middle].time
|
||||
if (time < middleTime) {
|
||||
right = middle - 1
|
||||
} else {
|
||||
if (middle + 1 >= mLrcEntryList.size || time < mLrcEntryList[middle + 1].time) {
|
||||
return middle
|
||||
}
|
||||
left = middle + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/** 获取当前在视图中央的行数 */
|
||||
private val centerLine: Int
|
||||
get() {
|
||||
var centerLine = 0
|
||||
var minDistance = Float.MAX_VALUE
|
||||
for (i in mLrcEntryList.indices) {
|
||||
if (abs(mOffset - getOffset(i)) < minDistance) {
|
||||
minDistance = abs(mOffset - getOffset(i))
|
||||
centerLine = i
|
||||
}
|
||||
}
|
||||
return centerLine
|
||||
}
|
||||
|
||||
/** 获取歌词距离视图顶部的距离 采用懒加载方式 */
|
||||
private fun getOffset(line: Int): Float {
|
||||
if (mLrcEntryList.isEmpty()) return 0F
|
||||
if (mLrcEntryList[line].offset == Float.MIN_VALUE) {
|
||||
var offset = (height / 2).toFloat()
|
||||
for (i in 1..line) {
|
||||
offset -= ((mLrcEntryList[i - 1].height + mLrcEntryList[i].height shr 1)
|
||||
+ mDividerHeight)
|
||||
}
|
||||
mLrcEntryList[line].offset = offset
|
||||
}
|
||||
return mLrcEntryList[line].offset
|
||||
}
|
||||
|
||||
/** 获取歌词宽度 */
|
||||
private val lrcWidth: Float
|
||||
get() = width - mLrcPadding * 2
|
||||
|
||||
/** 在主线程中运行 */
|
||||
private fun runOnUi(r: Runnable) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
r.run()
|
||||
} else {
|
||||
post(r)
|
||||
}
|
||||
}
|
||||
|
||||
/** 播放按钮点击监听器,点击后应该跳转到指定播放位置 */
|
||||
fun interface OnPlayClickListener {
|
||||
/**
|
||||
* 播放按钮被点击,应该跳转到指定播放位置
|
||||
*
|
||||
* @return 是否成功消费该事件,如果成功消费,则会更新UI
|
||||
*/
|
||||
fun onPlayClick(time: Long): Boolean
|
||||
}
|
||||
|
||||
fun interface OnFlingXListener {
|
||||
fun onFlingX(velocityX: Float): Boolean
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ADJUST_DURATION: Long = 100
|
||||
private const val TIMELINE_KEEP_TIME = 4 * DateUtils.SECOND_IN_MILLIS
|
||||
}
|
||||
|
||||
init {
|
||||
init(attrs)
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ interface Repository {
|
|||
suspend fun genresHome(): Home
|
||||
suspend fun playlists(): Home
|
||||
suspend fun homeSections(): List<Home>
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
suspend fun homeSectionsFlow(): Flow<Result<List<Home>>>
|
||||
suspend fun playlist(playlistId: Long): Playlist
|
||||
|
@ -106,6 +107,7 @@ interface Repository {
|
|||
suspend fun searchArtists(query: String): List<Artist>
|
||||
suspend fun searchSongs(query: String): List<Song>
|
||||
suspend fun searchAlbums(query: String): List<Album>
|
||||
suspend fun isSongFavorite(songId: Long): Boolean
|
||||
fun getSongByGenre(genreId: Long): Song
|
||||
}
|
||||
|
||||
|
@ -133,6 +135,9 @@ class RealRepository(
|
|||
|
||||
override suspend fun searchAlbums(query: String): List<Album> = albumRepository.albums(query)
|
||||
|
||||
override suspend fun isSongFavorite(songId: Long): Boolean =
|
||||
roomRepository.isSongFavorite(context, songId)
|
||||
|
||||
override fun getSongByGenre(genreId: Long): Song = genreRepository.song(genreId)
|
||||
|
||||
override suspend fun searchArtists(query: String): List<Artist> =
|
||||
|
@ -150,7 +155,8 @@ class RealRepository(
|
|||
|
||||
override suspend fun artistById(artistId: Long): Artist = artistRepository.artist(artistId)
|
||||
|
||||
override suspend fun albumArtistByName(name: String): Artist = artistRepository.albumArtist(name)
|
||||
override suspend fun albumArtistByName(name: String): Artist =
|
||||
artistRepository.albumArtist(name)
|
||||
|
||||
override suspend fun recentArtists(): List<Artist> = lastAddedRepository.recentArtists()
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package code.name.monkey.retromusic.repository
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.LiveData
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.db.*
|
||||
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder.Companion.PLAYLIST_A_Z
|
||||
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder.Companion.PLAYLIST_SONG_COUNT
|
||||
|
@ -45,6 +47,7 @@ interface RoomRepository {
|
|||
suspend fun insertBlacklistPathAsync(blackListStoreEntity: BlackListStoreEntity)
|
||||
suspend fun blackListPaths(): List<BlackListStoreEntity>
|
||||
suspend fun deleteSongs(songs: List<Song>)
|
||||
suspend fun isSongFavorite(context: Context, songId: Long): Boolean
|
||||
}
|
||||
|
||||
class RealRoomRepository(
|
||||
|
@ -60,7 +63,7 @@ class RealRoomRepository(
|
|||
|
||||
@WorkerThread
|
||||
override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> =
|
||||
playlistDao.isPlaylistExists(playlistName)
|
||||
playlistDao.playlist(playlistName)
|
||||
|
||||
@WorkerThread
|
||||
override suspend fun playlists(): List<PlaylistEntity> = playlistDao.playlists()
|
||||
|
@ -111,12 +114,13 @@ class RealRoomRepository(
|
|||
}
|
||||
|
||||
override suspend fun favoritePlaylist(favorite: String): PlaylistEntity {
|
||||
val playlist: PlaylistEntity? = playlistDao.isPlaylistExists(favorite).firstOrNull()
|
||||
val playlist: PlaylistEntity? = playlistDao.playlist(favorite).firstOrNull()
|
||||
return if (playlist != null) {
|
||||
playlist
|
||||
} else {
|
||||
println("Playlist Created")
|
||||
createPlaylist(PlaylistEntity(playlistName = favorite))
|
||||
playlistDao.isPlaylistExists(favorite).first()
|
||||
playlistDao.playlist(favorite).first()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,12 +149,12 @@ class RealRoomRepository(
|
|||
|
||||
override fun favoritePlaylistLiveData(favorite: String): LiveData<List<SongEntity>> =
|
||||
playlistDao.favoritesSongsLiveData(
|
||||
playlistDao.isPlaylistExists(favorite).first().playListId
|
||||
playlistDao.playlist(favorite).first().playListId
|
||||
)
|
||||
|
||||
override suspend fun favoritePlaylistSongs(favorite: String): List<SongEntity> =
|
||||
if (playlistDao.isPlaylistExists(favorite).isNotEmpty()) playlistDao.favoritesSongs(
|
||||
playlistDao.isPlaylistExists(favorite).first().playListId
|
||||
if (playlistDao.playlist(favorite).isNotEmpty()) playlistDao.favoritesSongs(
|
||||
playlistDao.playlist(favorite).first().playListId
|
||||
) else emptyList()
|
||||
|
||||
override suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) =
|
||||
|
@ -192,4 +196,12 @@ class RealRoomRepository(
|
|||
blackListStoreDao.deleteBlacklistPath(blackListStoreEntity)
|
||||
|
||||
override suspend fun clearBlacklist() = blackListStoreDao.clearBlacklist()
|
||||
|
||||
override suspend fun isSongFavorite(context: Context, songId: Long): Boolean {
|
||||
return playlistDao.isSongExistsInPlaylist(
|
||||
playlistDao.playlist(context.getString(R.string.favorites)).firstOrNull()?.playListId
|
||||
?: -1,
|
||||
songId
|
||||
).isNotEmpty()
|
||||
}
|
||||
}
|
|
@ -24,6 +24,8 @@ import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION;
|
|||
import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
|
||||
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
|
||||
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
|
||||
import static code.name.monkey.retromusic.service.notification.PlayingNotification.NOTIFY_MODE_BACKGROUND;
|
||||
import static code.name.monkey.retromusic.service.notification.PlayingNotification.NOTIFY_MODE_FOREGROUND;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
|
@ -369,6 +371,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
private PowerManager.WakeLock wakeLock;
|
||||
private NotificationManager notificationManager;
|
||||
private boolean isForeground = false;
|
||||
private int notifyMode = NOTIFY_MODE_BACKGROUND;
|
||||
|
||||
private static Bitmap copy(Bitmap bitmap) {
|
||||
Bitmap.Config config = bitmap.getConfig();
|
||||
|
@ -938,8 +941,9 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
updateNotification();
|
||||
break;
|
||||
case CLASSIC_NOTIFICATION:
|
||||
initNotification();
|
||||
updateNotification();
|
||||
playingNotification.setPlaying(isPlaying());
|
||||
playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify);
|
||||
break;
|
||||
case TOGGLE_HEADSET:
|
||||
registerHeadsetEvents();
|
||||
|
@ -1456,9 +1460,18 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
}
|
||||
|
||||
private Unit startForegroundOrNotify() {
|
||||
if (!isForeground) {
|
||||
int newNotifyMode = isPlaying() ? NOTIFY_MODE_FOREGROUND : NOTIFY_MODE_BACKGROUND;
|
||||
|
||||
if (notifyMode != newNotifyMode && newNotifyMode == NOTIFY_MODE_BACKGROUND) {
|
||||
// This makes the notification dismissible
|
||||
// We can't call stopForeground(false) on A12 though, which may result in crashes
|
||||
// when we call startForeground after that e.g. when Alarm goes off,
|
||||
if (Build.VERSION.SDK_INT < VERSION_CODES.S) stopForeground(false);
|
||||
}
|
||||
|
||||
if (newNotifyMode == NOTIFY_MODE_FOREGROUND) {
|
||||
// Specify that this is a media service, if supported.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
if (VersionUtils.hasQ()) {
|
||||
startForeground(
|
||||
PlayingNotification.NOTIFICATION_ID, playingNotification.build(),
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
||||
|
@ -1474,6 +1487,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
PlayingNotification.NOTIFICATION_ID, playingNotification.build()
|
||||
);
|
||||
}
|
||||
notifyMode = newNotifyMode;
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ abstract class PlayingNotification(context: Context) :
|
|||
const val NOTIFICATION_CONTROLS_SIZE_MULTIPLIER = 1.0f
|
||||
internal const val NOTIFICATION_CHANNEL_ID = "playing_notification"
|
||||
const val NOTIFICATION_ID = 1
|
||||
const val NOTIFY_MODE_FOREGROUND = 1
|
||||
const val NOTIFY_MODE_BACKGROUND = 0
|
||||
|
||||
|
||||
@RequiresApi(26)
|
||||
|
|
|
@ -30,8 +30,6 @@ import androidx.media.app.NotificationCompat.MediaStyle
|
|||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
|
@ -163,6 +161,7 @@ class PlayingNotificationImpl(
|
|||
onUpdate()
|
||||
}
|
||||
})
|
||||
updateFavorite(song, onUpdate)
|
||||
}
|
||||
|
||||
private fun buildPlayAction(isPlaying: Boolean): NotificationCompat.Action {
|
||||
|
@ -185,17 +184,28 @@ class PlayingNotificationImpl(
|
|||
).build()
|
||||
}
|
||||
|
||||
private fun buildDismissAction(): NotificationCompat.Action {
|
||||
return NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_close,
|
||||
context.getString(R.string.customactivityoncrash_error_activity_error_details_close),
|
||||
retrievePlaybackAction(ACTION_QUIT)
|
||||
).build()
|
||||
}
|
||||
|
||||
override fun setPlaying(isPlaying: Boolean) {
|
||||
mActions[2] = buildPlayAction(isPlaying)
|
||||
// Show dismiss action if we are not playing but only for A12+, as we can't call stopForeground(false)
|
||||
// on A12 which would result in crashes when we call startForeground after that
|
||||
if (!isPlaying) {
|
||||
addAction(buildDismissAction())
|
||||
} else {
|
||||
if (mActions.size == 5) mActions.removeAt(4)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateFavorite(song: Song, onUpdate: () -> Unit) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val playlist: PlaylistEntity = MusicUtil.repository.favoritePlaylist()
|
||||
val isFavorite = if (playlist != null) {
|
||||
val songEntity = song.toSongEntity(playlist.playListId)
|
||||
MusicUtil.repository.isFavoriteSong(songEntity).isNotEmpty()
|
||||
} else false
|
||||
val isFavorite = MusicUtil.repository.isSongFavorite(song.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
mActions[0] = buildFavoriteAction(isFavorite)
|
||||
onUpdate()
|
||||
|
|
|
@ -15,12 +15,7 @@
|
|||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import code.name.monkey.retromusic.R
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.play.core.review.ReviewManagerFactory
|
||||
|
||||
object AppRater {
|
||||
|
|
|
@ -1,189 +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.util;
|
||||
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
/**
|
||||
* Created by hefuyi on 2016/11/8.
|
||||
*/
|
||||
public class LyricUtil {
|
||||
|
||||
private static final String lrcRootPath =
|
||||
android.os.Environment.getExternalStorageDirectory().toString() + "/RetroMusic/lyrics/";
|
||||
private static final String TAG = "LyricUtil";
|
||||
|
||||
@Nullable
|
||||
public static File writeLrcToLoc(
|
||||
@NonNull String title, @NonNull String artist, @NonNull String lrcContext) {
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
File file = new File(getLrcPath(title, artist));
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
writer = new FileWriter(getLrcPath(title, artist));
|
||||
writer.write(lrcContext);
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
try {
|
||||
if (writer != null) writer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//So in Retro, Lrc file can be same folder as Music File or in RetroMusic Folder
|
||||
// In this case we pass location of the file and Contents to write to file
|
||||
public static void writeLrc(@NonNull Song song, @NonNull String lrcContext) {
|
||||
FileWriter writer = null;
|
||||
File location;
|
||||
try {
|
||||
if (isLrcOriginalFileExist(song.getData())) {
|
||||
location = getLocalLyricOriginalFile(song.getData());
|
||||
} else if (isLrcFileExist(song.getTitle(), song.getArtistName())) {
|
||||
location = getLocalLyricFile(song.getTitle(), song.getArtistName());
|
||||
} else {
|
||||
location = new File(getLrcPath(song.getTitle(), song.getArtistName()));
|
||||
if (!location.getParentFile().exists()) {
|
||||
location.getParentFile().mkdirs();
|
||||
}
|
||||
}
|
||||
writer = new FileWriter(location);
|
||||
writer.write(lrcContext);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (writer != null) writer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean deleteLrcFile(@NonNull String title, @NonNull String artist) {
|
||||
File file = new File(getLrcPath(title, artist));
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
public static boolean isLrcFileExist(@NonNull String title, @NonNull String artist) {
|
||||
File file = new File(getLrcPath(title, artist));
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
public static boolean isLrcOriginalFileExist(@NonNull String path) {
|
||||
File file = new File(getLrcOriginalPath(path));
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getLocalLyricFile(@NonNull String title, @NonNull String artist) {
|
||||
File file = new File(getLrcPath(title, artist));
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getLocalLyricOriginalFile(@NonNull String path) {
|
||||
File file = new File(getLrcOriginalPath(path));
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getLrcPath(String title, String artist) {
|
||||
return lrcRootPath + title + " - " + artist + ".lrc";
|
||||
}
|
||||
|
||||
private static String getLrcOriginalPath(String filePath) {
|
||||
return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1), "lrc");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String decryptBASE64(@NonNull String str) {
|
||||
if (str == null || str.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
byte[] encode = str.getBytes(StandardCharsets.UTF_8);
|
||||
// base64 解密
|
||||
return new String(
|
||||
Base64.decode(encode, 0, encode.length, Base64.DEFAULT), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getStringFromFile(@NonNull String title, @NonNull String artist)
|
||||
throws Exception {
|
||||
File file = new File(getLrcPath(title, artist));
|
||||
FileInputStream fin = new FileInputStream(file);
|
||||
String ret = convertStreamToString(fin);
|
||||
fin.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static String convertStreamToString(InputStream is) throws Exception {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getStringFromLrc(File file) {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
Log.i("Error", "Error Occurred");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
188
app/src/main/java/code/name/monkey/retromusic/util/LyricUtil.kt
Normal file
188
app/src/main/java/code/name/monkey/retromusic/util/LyricUtil.kt
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* 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.util
|
||||
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.io.*
|
||||
|
||||
/**
|
||||
* Created by hefuyi on 2016/11/8.
|
||||
*/
|
||||
object LyricUtil {
|
||||
private val lrcRootPath =
|
||||
Environment.getExternalStorageDirectory().toString() + "/RetroMusic/lyrics/"
|
||||
private const val TAG = "LyricUtil"
|
||||
fun writeLrcToLoc(
|
||||
title: String, artist: String, lrcContext: String
|
||||
): File? {
|
||||
var writer: FileWriter? = null
|
||||
return try {
|
||||
val file = File(getLrcPath(title, artist))
|
||||
if (file.parentFile?.exists() != true) {
|
||||
file.parentFile?.mkdirs()
|
||||
}
|
||||
writer = FileWriter(getLrcPath(title, artist))
|
||||
writer.write(lrcContext)
|
||||
file
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
} finally {
|
||||
try {
|
||||
writer?.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//So in Retro, Lrc file can be same folder as Music File or in RetroMusic Folder
|
||||
// In this case we pass location of the file and Contents to write to file
|
||||
fun writeLrc(song: Song, lrcContext: String) {
|
||||
var writer: FileWriter? = null
|
||||
val location: File?
|
||||
try {
|
||||
if (isLrcOriginalFileExist(song.data)) {
|
||||
location = getLocalLyricOriginalFile(song.data)
|
||||
} else if (isLrcFileExist(song.title, song.artistName)) {
|
||||
location = getLocalLyricFile(song.title, song.artistName)
|
||||
} else {
|
||||
location = File(getLrcPath(song.title, song.artistName))
|
||||
if (location.parentFile?.exists() != true) {
|
||||
location.parentFile?.mkdirs()
|
||||
}
|
||||
}
|
||||
writer = FileWriter(location)
|
||||
writer.write(lrcContext)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
try {
|
||||
writer?.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteLrcFile(title: String, artist: String): Boolean {
|
||||
val file = File(getLrcPath(title, artist))
|
||||
return file.delete()
|
||||
}
|
||||
|
||||
private fun isLrcFileExist(title: String, artist: String): Boolean {
|
||||
val file = File(getLrcPath(title, artist))
|
||||
return file.exists()
|
||||
}
|
||||
|
||||
private fun isLrcOriginalFileExist(path: String): Boolean {
|
||||
val file = File(getLrcOriginalPath(path))
|
||||
return file.exists()
|
||||
}
|
||||
|
||||
private fun getLocalLyricFile(title: String, artist: String): File? {
|
||||
val file = File(getLrcPath(title, artist))
|
||||
return if (file.exists()) {
|
||||
file
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLocalLyricOriginalFile(path: String): File? {
|
||||
val file = File(getLrcOriginalPath(path))
|
||||
return if (file.exists()) {
|
||||
file
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLrcPath(title: String, artist: String): String {
|
||||
return "$lrcRootPath$title - $artist.lrc"
|
||||
}
|
||||
|
||||
private fun getLrcOriginalPath(filePath: String): String {
|
||||
return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1), "lrc")
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getStringFromFile(title: String, artist: String): String {
|
||||
val file = File(getLrcPath(title, artist))
|
||||
val fin = FileInputStream(file)
|
||||
val ret = convertStreamToString(fin)
|
||||
fin.close()
|
||||
return ret
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun convertStreamToString(`is`: InputStream): String {
|
||||
val reader = BufferedReader(InputStreamReader(`is`))
|
||||
val sb = StringBuilder()
|
||||
var line: String?
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
sb.append(line).append("\n")
|
||||
}
|
||||
reader.close()
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun getStringFromLrc(file: File?): String {
|
||||
try {
|
||||
val reader = BufferedReader(FileReader(file))
|
||||
val sb = StringBuilder()
|
||||
var line: String?
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
sb.append(line).append("\n")
|
||||
}
|
||||
reader.close()
|
||||
return sb.toString()
|
||||
} catch (e: Exception) {
|
||||
Log.i("Error", "Error Occurred")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getSyncedLyricsFile(song: Song): File? {
|
||||
return when {
|
||||
isLrcOriginalFileExist(song.data) -> {
|
||||
getLocalLyricOriginalFile(song.data)
|
||||
}
|
||||
isLrcFileExist(song.title, song.artistName) -> {
|
||||
getLocalLyricFile(song.title, song.artistName)
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getEmbeddedSyncedLyrics(data: String): String? {
|
||||
val embeddedLyrics = try{
|
||||
AudioFileIO.read(File(data)).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
|
||||
} catch(e: Exception){
|
||||
return null
|
||||
}
|
||||
return if (AbsSynchronizedLyrics.isSynchronized(embeddedLyrics)) {
|
||||
embeddedLyrics
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -561,6 +561,8 @@ object PreferenceUtil {
|
|||
}
|
||||
set(value) = sharedPreferences.edit {
|
||||
putInt(NOW_PLAYING_SCREEN_ID, value.id)
|
||||
// Also set a cover theme for that now playing
|
||||
value.defaultCoverTheme?.let { coverTheme -> albumCoverStyle = coverTheme }
|
||||
}
|
||||
|
||||
val albumCoverTransform: ViewPager.PageTransformer
|
||||
|
@ -655,4 +657,7 @@ object PreferenceUtil {
|
|||
|
||||
val materialYou
|
||||
get() = sharedPreferences.getBoolean(MATERIAL_YOU, VersionUtils.hasS())
|
||||
|
||||
val isSnowFalling
|
||||
get() = sharedPreferences.getBoolean(SNOWFALL, false)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
|
@ -32,14 +31,6 @@ class RingtoneManager(val context: Context) {
|
|||
fun setRingtone(song: Song) {
|
||||
val resolver = context.contentResolver
|
||||
val uri = getSongFileUri(song.id)
|
||||
try {
|
||||
val values = ContentValues(2)
|
||||
values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, "1")
|
||||
values.put(MediaStore.Audio.AudioColumns.IS_ALARM, "1")
|
||||
resolver.update(uri, values, null, null)
|
||||
} catch (ignored: UnsupportedOperationException) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val cursor = resolver.query(
|
||||
|
|
|
@ -26,7 +26,6 @@ import androidx.core.graphics.BlendModeCompat.SRC_IN
|
|||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
|
||||
object ViewUtil {
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue