Merge pull request #1383 from prathameshmm02/dev

Bug fixes
This commit is contained in:
Daksh P. Jain 2022-06-20 20:52:42 +05:30 committed by GitHub
commit faf065ea73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
96 changed files with 1316 additions and 1773 deletions

View file

@ -38,12 +38,12 @@ ___
## 📱 Screenshots
### App Themes
| <img src="screenshots/home_light.jpg" width="200"/> | <img src="screenshots/home_dark.jpg" width="200"/> | <img src="screenshots/home_black.jpg" width="200"/> |
| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg" width="200"/> | <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg" width="200"/> | <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/4.jpg" width="200"/> |
|:---:|:---:|:---:|
|Clearly white| Kinda dark | Just black|
### Player screen
| <img src="screenshots/home_light.jpg" width="200"/>| <img src="screenshots/songs.jpg" width="200"/>| <img src="screenshots/albums.jpg" width="200"/>| <img src="screenshots/artists.jpg" width="200"/>| <img src="screenshots/settings.jpg" width="200"/>|
| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/5.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/6.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/7.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/8.jpg" width="200"/>|
|:---:|:---:|:---:|:---:|:---:|
| Home | Songs | Albums | Artists | Settings |
@ -58,7 +58,7 @@ ___
| Synced Replace Cover light | Synced Replace Cover dark | Synced Replace Cover black |
### 10+ Now playing themes
| <img src="screenshots/normal.jpg" width="200"/> |<img src="screenshots/fit.jpg" width="200"/>| <img src="screenshots/flat.jpg" width="200"/> | <img src="screenshots/color.jpg" width="200"/> | <img src="screenshots/material.jpg" width="200"/> |
| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg" width="200"/> |<img src="screenshots/fit.jpg" width="200"/>| <img src="screenshots/flat.jpg" width="200"/> | <img src="screenshots/color.jpg" width="200"/> | <img src="screenshots/material.jpg" width="200"/> |
|:-----: |:-----: |:-----: |:-----: |:-----: |
| Normal | Fit | Flat | Color | Material |

View file

@ -14,8 +14,8 @@ android {
vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic"
versionCode 10592
versionName '6.0.1-beta'
versionCode 10596
versionName '6.0.2-beta'
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
}
@ -41,6 +41,15 @@ android {
versionNameSuffix ' DEBUG'
}
}
flavorDimensions "version"
productFlavors {
normal {
dimension "version"
}
fdroid {
dimension "version"
}
}
buildFeatures{
viewBinding true
@ -88,18 +97,18 @@ dependencies {
implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.annotation:annotation:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "androidx.preference:preference-ktx:$preference_version"
implementation 'androidx.core:core-ktx:1.8.0'
implementation "androidx.core:core-ktx:$core_version"
implementation 'androidx.palette:palette-ktx:1.0.0'
//Cast Dependencies
implementation 'androidx.mediarouter:mediarouter:1.3.0'
implementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
//Cast Dependencies
normalImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
//WebServer by NanoHttpd
implementation "org.nanohttpd:nanohttpd:2.3.1"
normalImplementation "org.nanohttpd:nanohttpd:2.3.1"
implementation "androidx.navigation:navigation-runtime-ktx:$navigation_version"
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
@ -110,22 +119,21 @@ dependencies {
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
def lifecycle_version = "2.5.0-rc01"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.core:core-splashscreen:1.0.0-rc01"
implementation 'com.google.android.play:feature-delivery:2.0.0'
implementation 'com.google.android.play:review:2.0.0'
normalImplementation 'com.google.android.play:feature-delivery:2.0.0'
normalImplementation 'com.google.android.play:review:2.0.0'
implementation "com.google.android.material:material:$mdc_version"
def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.8'
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.9'
def material_dialog_version = "3.3.0"
implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
@ -160,7 +168,7 @@ dependencies {
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
implementation 'com.github.Adonai:jaudiotagger:2.3.15'
implementation 'com.anjlab.android.iab.v3:library:2.0.3'
normalImplementation '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'

View file

@ -0,0 +1,5 @@
package code.name.monkey.retromusic.activities.base
abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
}

View file

@ -0,0 +1,12 @@
package code.name.monkey.retromusic.billing
import android.content.Context
@Suppress("UNUSED_PARAMETER")
class BillingManager(context: Context) {
fun release() {}
val isProVersion: Boolean
get() = true
}

View file

@ -0,0 +1,6 @@
package code.name.monkey.retromusic.cast
import android.content.Context
@Suppress("UNUSED_PARAMETER")
class RetroWebServer(context: Context)

View file

@ -0,0 +1,15 @@
@file:Suppress("UNUSED_PARAMETER", "unused")
package code.name.monkey.retromusic.extensions
import android.content.Context
import android.view.Menu
import androidx.fragment.app.FragmentActivity
fun Context.setUpMediaRouteButton(menu: Menu) {}
fun FragmentActivity.installLanguageAndRecreate(code: String) {}
fun Context.goToProVersion() {}
fun Context.installSplitCompat() {}

View file

@ -0,0 +1,47 @@
package code.name.monkey.retromusic.service
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.service.playback.Playback
// Empty CastPlayer implementation
class CastPlayer : Playback {
override val isInitialized: Boolean
get() = true
override val isPlaying: Boolean
get() = true
override val audioSessionId: Int
get() = 0
override fun setDataSource(
song: Song,
force: Boolean,
completion: (success: Boolean) -> Unit,
) {
}
override fun setNextDataSource(path: String?) {}
override var callbacks: Playback.PlaybackCallbacks? = null
override fun start() = true
override fun stop() {}
override fun release() {}
override fun pause(): Boolean = true
override fun duration() = 0
override fun position() = 0
override fun seek(whereto: Int) = whereto
override fun setVolume(vol: Float) = true
override fun setAudioSessionId(sessionId: Int) = true
override fun setCrossFadeDuration(duration: Int) {}
override fun setPlaybackSpeedPitch(speed: Float, pitch: Float) {}
}

View file

@ -0,0 +1,8 @@
package code.name.monkey.retromusic.util
import android.content.Context
@Suppress("UNUSED_PARAMETER")
object AppRater {
fun appLaunched(context: Context) {}
}

View file

@ -22,7 +22,6 @@
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission
android:name="android.permission.BLUETOOTH_CONNECT"
android:usesPermissionFlags="neverForLocation"
@ -120,7 +119,6 @@
<activity android:name=".activities.tageditor.SongTagEditorActivity" />
<activity android:name=".activities.SupportDevelopmentActivity" />
<activity android:name=".activities.LicenseActivity" />
<activity android:name=".activities.PurchaseActivity" />
<activity android:name=".activities.bugreport.BugReportActivity" />
<activity android:name=".activities.ShareInstagramStory" />
<activity android:name=".activities.DriveModeActivity" />
@ -315,10 +313,6 @@
android:name="com.lge.support.SPLIT_WINDOW"
android:value="true" />
<meta-data
android:name="com.android.vending.splits.required"
android:value="true" />
<!-- Android Auto -->
<meta-data
android:name="com.google.android.gms.car.application"
@ -329,11 +323,6 @@
<meta-data
android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification" />
<!-- ChromeCast -->
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="code.name.monkey.retromusic.cast.CastOptionsProvider" />
</application>
<!--

View file

@ -62,6 +62,14 @@
</head>
<body>
<div>
<h5>June 21, 2022</h5>
<h2>v6.0.2<span class="tag"><i>Beta</i></span></h2>
<h3>Fixed</h3>
<ul>
<li>Minor bug fixes and improvements</li>
</ul>
</div>
<div>
<h5>June 13, 2022</h5>
<h2>v6.0.1<span class="tag"><i>Beta</i></span></h2>

View file

@ -19,20 +19,17 @@ import androidx.preference.PreferenceManager
import cat.ereza.customactivityoncrash.config.CaocConfig
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
import code.name.monkey.retromusic.activities.ErrorActivity
import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.billing.BillingManager
import code.name.monkey.retromusic.helper.WallpaperAccentManager
import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.PurchaseInfo
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
class App : Application() {
lateinit var billingProcessor: BillingProcessor
lateinit var billingManager: BillingManager
private val wallpaperAccentManager = WallpaperAccentManager(this)
override fun onCreate() {
@ -55,33 +52,20 @@ class App : Application() {
if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).initDynamicShortcuts()
// automatically restores purchases
billingProcessor = BillingProcessor(
this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
object : BillingProcessor.IBillingHandler {
override fun onProductPurchased(productId: String, details: PurchaseInfo?) {}
override fun onPurchaseHistoryRestored() {
showToast(R.string.restored_previous_purchase_please_restart)
}
override fun onBillingError(errorCode: Int, error: Throwable?) {}
override fun onBillingInitialized() {}
})
billingManager = BillingManager(this)
// setting Error activity
CaocConfig.Builder.create().errorActivity(ErrorActivity::class.java)
.restartActivity(MainActivity::class.java).apply()
// Set Default values for now playing preferences
// This will reduce start time for now playing settings fragment as Preference listener of AbsSlidingMusicPanelActivity won't be called
// This will reduce startup time for now playing settings fragment as Preference listener of AbsSlidingMusicPanelActivity won't be called
PreferenceManager.setDefaultValues(this, R.xml.pref_now_playing_screen, false)
}
override fun onTerminate() {
super.onTerminate()
billingProcessor.release()
billingManager.release()
wallpaperAccentManager.release()
}
@ -93,9 +77,7 @@ class App : Application() {
}
fun isProVersion(): Boolean {
return BuildConfig.DEBUG || instance?.billingProcessor!!.isPurchased(
PRO_VERSION_PRODUCT_ID
)
return BuildConfig.DEBUG || instance?.billingManager!!.isProVersion
}
}
}

View file

@ -136,7 +136,6 @@ const val ARTIST_DETAIL_SONG_SORT_ORDER = "artist_detail_song_sort_order"
const val LYRICS_OPTIONS = "lyrics_tab_position"
const val CHOOSE_EQUALIZER = "choose_equalizer"
const val EQUALIZER = "equalizer"
const val TOGGLE_SHUFFLE = "toggle_shuffle"
const val SONG_GRID_STYLE = "song_grid_style"
const val PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume"
const val FILTER_SONG = "filter_song"

View file

@ -14,41 +14,23 @@
*/
package code.name.monkey.retromusic.activities
import android.graphics.Paint
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.databinding.ActivityDonationBinding
import code.name.monkey.retromusic.databinding.ItemDonationOptionBinding
import code.name.monkey.retromusic.extensions.*
import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.PurchaseInfo
import com.anjlab.android.iab.v3.SkuDetails
import code.name.monkey.retromusic.extensions.setStatusBarColorAuto
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.extensions.surfaceColor
class SupportDevelopmentActivity : AbsThemeActivity(), BillingProcessor.IBillingHandler {
class SupportDevelopmentActivity : AbsThemeActivity() {
lateinit var binding: ActivityDonationBinding
companion object {
val TAG: String = SupportDevelopmentActivity::class.java.simpleName
const val DONATION_PRODUCT_IDS = R.array.donation_ids
}
var billingProcessor: BillingProcessor? = null
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
@ -57,11 +39,6 @@ class SupportDevelopmentActivity : AbsThemeActivity(), BillingProcessor.IBilling
return super.onOptionsItemSelected(item)
}
fun donate(i: Int) {
val ids = resources.getStringArray(DONATION_PRODUCT_IDS)
billingProcessor?.purchase(this, ids[i])
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDonationBinding.inflate(layoutInflater)
@ -72,148 +49,11 @@ class SupportDevelopmentActivity : AbsThemeActivity(), BillingProcessor.IBilling
setupToolbar()
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
TintHelper.setTint(binding.progress, accentColor())
binding.donation.setTextColor(accentColor())
}
private fun setupToolbar() {
val toolbarColor = surfaceColor()
binding.toolbar.setBackgroundColor(toolbarColor)
binding.toolbar.setBackgroundColor(surfaceColor())
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
setSupportActionBar(binding.toolbar)
}
override fun onBillingInitialized() {
loadSkuDetails()
}
private fun loadSkuDetails() {
binding.progressContainer.isVisible = true
binding.recyclerView.isVisible = false
val ids = resources.getStringArray(DONATION_PRODUCT_IDS)
billingProcessor!!.getPurchaseListingDetailsAsync(
ArrayList(listOf(*ids)),
object : BillingProcessor.ISkuDetailsResponseListener {
override fun onSkuDetailsResponse(skuDetails: MutableList<SkuDetails>?) {
if (skuDetails == null || skuDetails.isEmpty()) {
binding.progressContainer.isVisible = false
return
}
binding.progressContainer.isVisible = false
binding.recyclerView.apply {
itemAnimator = DefaultItemAnimator()
layoutManager = GridLayoutManager(this@SupportDevelopmentActivity, 2)
adapter = SkuDetailsAdapter(this@SupportDevelopmentActivity, skuDetails)
isVisible = true
}
}
override fun onSkuDetailsError(error: String?) {
Log.e(TAG, error.toString())
}
})
}
override fun onProductPurchased(productId: String, details: PurchaseInfo?) {
// loadSkuDetails();
showToast(R.string.thank_you)
}
override fun onBillingError(errorCode: Int, error: Throwable?) {
Log.e(TAG, "Billing error: code = $errorCode", error)
}
override fun onPurchaseHistoryRestored() {
// loadSkuDetails();
showToast(R.string.restored_previous_purchases)
}
override fun onDestroy() {
billingProcessor?.release()
super.onDestroy()
}
}
class SkuDetailsAdapter(
private var donationsDialog: SupportDevelopmentActivity,
objects: List<SkuDetails>,
) : RecyclerView.Adapter<SkuDetailsAdapter.ViewHolder>() {
private var skuDetailsList: List<SkuDetails> = ArrayList()
init {
skuDetailsList = objects
}
private fun getIcon(position: Int): Int {
return when (position) {
0 -> R.drawable.ic_cookie
1 -> R.drawable.ic_take_away
2 -> R.drawable.ic_take_away_coffe
3 -> R.drawable.ic_beer
4 -> R.drawable.ic_fast_food_meal
5 -> R.drawable.ic_popcorn
6 -> R.drawable.ic_card_giftcard
7 -> R.drawable.ic_food_croissant
else -> R.drawable.ic_card_giftcard
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
return ViewHolder(
ItemDonationOptionBinding.inflate(
LayoutInflater.from(donationsDialog),
viewGroup,
false
)
)
}
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
val skuDetails = skuDetailsList[i]
with(viewHolder.binding) {
itemTitle.text = skuDetails.title.replace("(Retro Music Player MP3 Player)", "")
.trim { it <= ' ' }
itemText.text = skuDetails.description
itemText.isVisible = false
itemPrice.text = skuDetails.priceText
itemImage.setImageResource(getIcon(i))
}
val purchased = donationsDialog.billingProcessor!!.isPurchased(skuDetails.productId)
val titleTextColor = if (purchased) ATHUtil.resolveColor(
donationsDialog,
android.R.attr.textColorHint
) else donationsDialog.textColorPrimary()
val contentTextColor =
if (purchased) titleTextColor else donationsDialog.textColorSecondary()
with(viewHolder.binding) {
itemTitle.setTextColor(titleTextColor)
itemText.setTextColor(contentTextColor)
itemPrice.setTextColor(titleTextColor)
strikeThrough(itemTitle, purchased)
strikeThrough(itemText, purchased)
strikeThrough(itemPrice, purchased)
}
viewHolder.itemView.isEnabled = !purchased
viewHolder.itemView.setOnClickListener { donationsDialog.donate(i) }
}
override fun getItemCount(): Int {
return skuDetailsList.size
}
class ViewHolder(val binding: ItemDonationOptionBinding) : RecyclerView.ViewHolder(binding.root)
companion object {
private fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
textView.paintFlags =
if (strikeThrough) textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
else textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
}
}

View file

@ -234,8 +234,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
navigationView.labelVisibilityMode = PreferenceUtil.tabTitleMode
}
TOGGLE_FULL_SCREEN -> {
if (!PreferenceUtil.isFullScreenMode) exitFullscreen()
setEdgeToEdgeOrImmersive()
recreate()
}
SCREEN_ON_LYRICS -> {
keepScreenOn(bottomSheetBehavior.state == STATE_EXPANDED && PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics || PreferenceUtil.isScreenOnEnabled)

View file

@ -31,7 +31,6 @@ import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.maybeShowAnnoyingToasts
import code.name.monkey.retromusic.util.theme.getNightMode
import code.name.monkey.retromusic.util.theme.getThemeResValue
import com.google.android.play.core.splitcompat.SplitCompat
import java.util.*
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
@ -105,6 +104,6 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
Locale.forLanguageTag(code)
}
super.attachBaseContext(LanguageContextWrapper.wrap(newBase, locale))
SplitCompat.install(this)
installSplitCompat()
}
}

View file

@ -42,7 +42,7 @@ class PlayingQueueAdapter(
activity: FragmentActivity,
dataSet: MutableList<Song>,
private var current: Int,
itemLayoutRes: Int
itemLayoutRes: Int,
) : SongAdapter(
activity, dataSet, itemLayoutRes, null
), DraggableItemAdapter<PlayingQueueAdapter.ViewHolder>,
@ -153,6 +153,14 @@ class PlayingQueueAdapter(
dragView?.isVisible = true
}
override fun onClick(v: View?) {
if (isInQuickSelectMode) {
toggleChecked(layoutPosition)
} else {
MusicPlayerRemote.playSongAt(layoutPosition)
}
}
override fun onSongMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_remove_from_playing_queue -> {
@ -209,7 +217,7 @@ class PlayingQueueAdapter(
internal class SwipedResultActionRemoveItem(
private val adapter: PlayingQueueAdapter,
private val position: Int,
private val activity: FragmentActivity
private val activity: FragmentActivity,
) : SwipeResultActionRemoveItem() {
private var songToRemove: Song? = null

View file

@ -1,43 +0,0 @@
@file:Suppress("unused")
package code.name.monkey.retromusic.cast
import android.content.Context
import code.name.monkey.retromusic.activities.MainActivity
import com.google.android.gms.cast.CastMediaControlIntent
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.SessionProvider
import com.google.android.gms.cast.framework.media.CastMediaOptions
import com.google.android.gms.cast.framework.media.MediaIntentReceiver
import com.google.android.gms.cast.framework.media.NotificationOptions
class CastOptionsProvider : OptionsProvider {
override fun getCastOptions(context: Context): CastOptions {
val buttonActions: MutableList<String> = ArrayList()
buttonActions.add(MediaIntentReceiver.ACTION_SKIP_PREV)
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK)
buttonActions.add(MediaIntentReceiver.ACTION_SKIP_NEXT)
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING)
val compatButtonActionsIndices = intArrayOf(1, 3)
val notificationOptions = NotificationOptions.Builder()
.setActions(buttonActions, compatButtonActionsIndices)
.setTargetActivityClassName(MainActivity::class.java.name)
.build()
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.setExpandedControllerActivityClassName(MainActivity::class.java.name)
.build()
return CastOptions.Builder()
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
.setCastMediaOptions(mediaOptions)
.build()
}
override fun getAdditionalSessionProviders(context: Context): MutableList<SessionProvider>? {
return null
}
}

View file

@ -95,7 +95,7 @@ class SleepTimerDialog : DialogFragment() {
shouldFinishLastSong.isVisible = false
timerUpdater.start()
setPositiveButton(android.R.string.ok, null)
setNegativeButton(R.string.cast_stop) { _, _ ->
setNegativeButton(R.string.action_cancel) { _, _ ->
timerUpdater.cancel()
val previous = makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE)
if (previous != null) {

View file

@ -14,9 +14,11 @@
*/
package code.name.monkey.retromusic.extensions
import android.R
import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.res.ColorStateList
import android.graphics.drawable.BitmapDrawable
import android.view.LayoutInflater
import android.view.View
@ -25,6 +27,7 @@ import android.view.ViewTreeObserver
import android.view.animation.AnimationUtils
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.annotation.ColorInt
import androidx.annotation.LayoutRes
import androidx.annotation.Px
import androidx.core.animation.doOnEnd
@ -67,6 +70,15 @@ fun EditText.appHandleColor(): EditText {
return this
}
fun NavigationBarView.setItemColors(@ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val csl = ColorStateList(
arrayOf(intArrayOf(-R.attr.state_checked), intArrayOf(R.attr.state_checked)),
intArrayOf(normalColor, selectedColor)
)
itemIconTintList = csl
itemTextColor = csl
}
/**
* Potentially animate showing a [BottomNavigationView].
*

View file

@ -24,6 +24,7 @@ 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.setUpMediaRouteButton
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType
@ -41,7 +42,6 @@ import com.afollestad.materialcab.attached.AttachedCab
import com.afollestad.materialcab.attached.destroy
import com.afollestad.materialcab.attached.isActive
import com.afollestad.materialcab.createCab
import com.google.android.gms.cast.framework.CastButtonFactory
class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
IAlbumClickListener, ICabHolder {
@ -169,7 +169,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
setupLayoutMenu(layoutItem.subMenu)
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
//Setting up cast button
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
requireContext().setUpMediaRouteButton(menu)
}
private fun setUpSortOrderMenu(

View file

@ -25,6 +25,7 @@ import code.name.monkey.retromusic.EXTRA_ARTIST_ID
import code.name.monkey.retromusic.EXTRA_ARTIST_NAME
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
import code.name.monkey.retromusic.extensions.setUpMediaRouteButton
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType
@ -43,7 +44,6 @@ import com.afollestad.materialcab.attached.AttachedCab
import com.afollestad.materialcab.attached.destroy
import com.afollestad.materialcab.attached.isActive
import com.afollestad.materialcab.createCab
import com.google.android.gms.cast.framework.CastButtonFactory
class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
IArtistClickListener, IAlbumArtistClickListener, ICabHolder {
@ -180,7 +180,7 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
setupAlbumArtistMenu(menu)
//Setting up cast button
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
requireContext().setUpMediaRouteButton(menu)
}
private fun setupAlbumArtistMenu(menu: Menu) {

View file

@ -26,12 +26,12 @@ 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.setUpMediaRouteButton
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.RetroUtil
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.transition.MaterialSharedAxis
class
@ -67,7 +67,7 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
menu.removeItem(R.id.action_sort_order)
menu.findItem(R.id.action_settings).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
//Setting up cast button
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
requireContext().setUpMediaRouteButton(menu)
}
override fun onResume() {

View file

@ -35,10 +35,7 @@ import code.name.monkey.retromusic.adapter.HomeAdapter
import code.name.monkey.retromusic.databinding.FragmentHomeBinding
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.dip
import code.name.monkey.retromusic.extensions.drawNextToNavbar
import code.name.monkey.retromusic.extensions.elevatedAccentColor
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.GlideApp
@ -48,7 +45,6 @@ import code.name.monkey.retromusic.interfaces.IScrollHelper
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.PreferenceUtil.userName
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis
@ -212,7 +208,7 @@ class HomeFragment :
ATHToolbarActivity.getToolbarBackgroundColor(binding.toolbar)
)
//Setting up cast button
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
requireContext().setUpMediaRouteButton(menu)
}
override fun scrollToTop() {

View file

@ -30,11 +30,11 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentLibraryBinding
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
import code.name.monkey.retromusic.extensions.setUpMediaRouteButton
import code.name.monkey.retromusic.extensions.whichFragment
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.model.CategoryInfo
import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.gms.cast.framework.CastButtonFactory
class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
@ -99,7 +99,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
getToolbarBackgroundColor(binding.toolbar)
)
//Setting up cast button
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
requireContext().setUpMediaRouteButton(menu)
}
override fun onMenuItemSelected(item: MenuItem): Boolean {

View file

@ -12,11 +12,10 @@
* See the GNU General Public License for more details.
*
*/
package code.name.monkey.retromusic.fragments.other
package code.name.monkey.retromusic.fragments.lyrics
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
@ -26,23 +25,19 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.findNavController
import androidx.transition.Fade
import androidx.viewpager2.adapter.FragmentStateAdapter
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
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.activities.tageditor.TagWriter
import code.name.monkey.retromusic.databinding.FragmentLyricsBinding
import code.name.monkey.retromusic.databinding.FragmentNormalLyricsBinding
import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.materialDialog
import code.name.monkey.retromusic.extensions.openUrl
import code.name.monkey.retromusic.extensions.uri
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.lyrics.LrcView
@ -52,7 +47,6 @@ import code.name.monkey.retromusic.util.FileUtils
import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.UriUtil
import com.afollestad.materialdialogs.input.input
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.jaudiotagger.audio.AudioFileIO
@ -60,14 +54,23 @@ import org.jaudiotagger.tag.FieldKey
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.collections.set
class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics),
MusicProgressViewUpdateHelper.Callback {
private var _binding: FragmentLyricsBinding? = null
private val binding get() = _binding!!
private lateinit var song: Song
private lateinit var lyricsSectionsAdapter: LyricsSectionsAdapter
private lateinit var normalLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var editSyncedLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var cacheFile: File
private var syncedLyrics: String = ""
private lateinit var syncedFileUri: Uri
private var lyricsType: LyricsType = LyricsType.NORMAL_LYRICS
private val googleSearchLrcUrl: String
get() {
@ -78,13 +81,7 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
return baseUrl
}
private lateinit var normalLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var newSyncedLyricsLauncher: ActivityResultLauncher<Intent>
private lateinit var editSyncedLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var cacheFile: File
private var syncedLyrics: String = ""
private lateinit var syncedFileUri: Uri
private lateinit var updateHelper: MusicProgressViewUpdateHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -95,14 +92,6 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
FileUtils.copyFileToUri(requireContext(), cacheFile, song.uri)
}
}
newSyncedLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
context?.contentResolver?.openOutputStream(result.data?.data!!)?.use {
it.write(syncedLyrics.toByteArray())
}
}
}
editSyncedLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
@ -119,36 +108,42 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
super.onViewCreated(view, savedInstanceState)
enterTransition = Fade()
exitTransition = Fade()
updateTitleSong()
lyricsSectionsAdapter = LyricsSectionsAdapter(requireActivity())
_binding = FragmentLyricsBinding.bind(view)
binding.container.transitionName = "lyrics"
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
updateTitleSong()
setupLyricsView()
loadLyrics()
setupWakelock()
setupViews()
setupToolbar()
}
private fun setupViews() {
binding.lyricsPager.adapter = lyricsSectionsAdapter
TabLayoutMediator(binding.tabLyrics, binding.lyricsPager) { tab, position ->
tab.text = when (position) {
0 -> getString(R.string.synced_lyrics)
1 -> getString(R.string.normal_lyrics)
else -> ""
private fun setupLyricsView() {
binding.lyricsView.apply {
setCurrentColor(accentColor())
setTimeTextColor(accentColor())
setTimelineColor(accentColor())
setTimelineTextColor(accentColor())
setDraggable(true, LrcView.OnPlayClickListener {
MusicPlayerRemote.seekTo(it.toInt())
return@OnPlayClickListener true
})
}
}
}.attach()
// lyricsPager.isUserInputEnabled = false
binding.tabLyrics.setSelectedTabIndicatorColor(accentColor())
binding.tabLyrics.setTabTextColors(textColorSecondary(), accentColor())
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.lyricsView.updateTime(progress.toLong())
}
private fun setupViews() {
binding.editButton.accentColor()
binding.editButton.setOnClickListener {
when (binding.lyricsPager.currentItem) {
0 -> {
when (lyricsType) {
LyricsType.SYNCED_LYRICS -> {
editSyncedLyrics()
}
1 -> {
LyricsType.NORMAL_LYRICS -> {
editNormalLyrics()
}
}
@ -158,11 +153,13 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateTitleSong()
loadLyrics()
}
override fun onServiceConnected() {
super.onServiceConnected()
updateTitleSong()
loadLyrics()
}
private fun updateTitleSong() {
@ -182,7 +179,7 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
}
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_search, menu)
inflater.inflate(R.menu.menu_lyrics, menu)
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
requireContext(),
binding.toolbar,
@ -198,17 +195,18 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
return false
}
@SuppressLint("CheckResult")
private fun editNormalLyrics() {
var content = ""
val file = File(MusicPlayerRemote.currentSong.data)
try {
content = AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
private fun editNormalLyrics(lyrics: String? = null) {
val file = File(song.data)
val content = lyrics ?: try {
AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
} catch (e: Exception) {
e.printStackTrace()
""
}
val song = song
materialDialog().show {
title(res = R.string.edit_normal_lyrics)
input(
@ -218,7 +216,6 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
) { _, input ->
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
syncedLyrics = input.toString()
GlobalScope.launch {
if (VersionUtils.hasR()) {
cacheFile = TagWriter.writeTagsToFilesR(
@ -245,7 +242,7 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
}
}
positiveButton(res = R.string.save) {
(lyricsSectionsAdapter.fragments[1].first as NormalLyrics).loadNormalLyrics()
loadNormalLyrics()
}
negativeButton(res = android.R.string.cancel)
}
@ -253,9 +250,10 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
@SuppressLint("CheckResult")
private fun editSyncedLyrics() {
val content: String = LyricUtil.getStringFromLrc(LyricUtil.getSyncedLyricsFile(song))
private fun editSyncedLyrics(lyrics: String? = null) {
val content = lyrics ?: LyricUtil.getStringFromLrc(LyricUtil.getSyncedLyricsFile(song))
val song = song
materialDialog().show {
title(res = R.string.edit_synced_lyrics)
input(
@ -278,57 +276,37 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
IntentSenderRequest.Builder(pendingIntent).build()
)
} else {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
intent.putExtra(
Intent.EXTRA_TITLE,
LyricUtil.getLrcOriginalPath(File(song.data).name)
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
GlobalScope.launch {
cacheFile = TagWriter.writeTagsToFilesR(
requireContext(),
AudioTagInfo(listOf(song.data), fieldKeyValueMap, null)
)[0]
val pendingIntent = MediaStore.createWriteRequest(
requireContext().contentResolver,
listOf(song.uri)
)
newSyncedLyricsLauncher.launch(intent)
normalLyricsLauncher.launch(
IntentSenderRequest.Builder(pendingIntent).build()
)
}
}
} else {
LyricUtil.writeLrc(song, input.toString())
}
}
positiveButton(res = R.string.save) {
(lyricsSectionsAdapter.fragments[0].first as SyncedLyrics).loadLRCLyrics()
loadLRCLyrics()
}
negativeButton(res = android.R.string.cancel)
}
}
class LyricsSectionsAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
val fragments = listOf(
Pair(SyncedLyrics(), R.string.synced_lyrics),
Pair(NormalLyrics(), R.string.normal_lyrics)
)
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position].first
}
}
class NormalLyrics : AbsMusicServiceFragment(R.layout.fragment_normal_lyrics) {
private var _binding: FragmentNormalLyricsBinding? = null
private val binding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
_binding = FragmentNormalLyricsBinding.bind(view)
loadNormalLyrics()
super.onViewCreated(view, savedInstanceState)
}
fun loadNormalLyrics() {
private fun loadNormalLyrics() {
var lyrics: String? = null
val file = File(MusicPlayerRemote.currentSong.data)
val file = File(song.data)
try {
lyrics = AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
} catch (e: Exception) {
@ -338,71 +316,38 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
binding.normalLyrics.text = lyrics
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
loadNormalLyrics()
}
override fun onServiceConnected() {
super.onServiceConnected()
loadNormalLyrics()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
class SyncedLyrics : AbsMusicServiceFragment(R.layout.fragment_synced_lyrics),
MusicProgressViewUpdateHelper.Callback {
private var _binding: FragmentSyncedLyricsBinding? = null
private val binding get() = _binding!!
private lateinit var updateHelper: MusicProgressViewUpdateHelper
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
_binding = FragmentSyncedLyricsBinding.bind(view)
setupLyricsView()
loadLRCLyrics()
super.onViewCreated(view, savedInstanceState)
}
fun loadLRCLyrics() {
/**
* @return success
*/
private fun loadLRCLyrics(): Boolean {
binding.lyricsView.setLabel(getString(R.string.empty))
LyricUtil.getSyncedLyricsFile(MusicPlayerRemote.currentSong)?.let {
binding.lyricsView.loadLrc(it)
val lrcFile = LyricUtil.getSyncedLyricsFile(song)
if (lrcFile != null) {
binding.lyricsView.loadLrc(lrcFile)
println("File: ${lrcFile.absolutePath}")
} else {
val embeddedLyrics = LyricUtil.getEmbeddedSyncedLyrics(song.data)
if (embeddedLyrics != null) {
binding.lyricsView.loadLrc(embeddedLyrics)
println("Lyrics: ${embeddedLyrics.substring(0..50)}")
} else {
return false
}
}
return true
}
private fun setupLyricsView() {
binding.lyricsView.apply {
setCurrentColor(accentColor())
setTimeTextColor(accentColor())
setTimelineColor(accentColor())
setTimelineTextColor(accentColor())
setDraggable(true, LrcView.OnPlayClickListener {
MusicPlayerRemote.seekTo(it.toInt())
return@OnPlayClickListener true
})
private fun loadLyrics() {
lyricsType = if (!loadLRCLyrics()) {
loadNormalLyrics()
LyricsType.SYNCED_LYRICS
} else {
binding.noLyricsFound.isVisible = false
binding.normalLyrics.text = ""
LyricsType.NORMAL_LYRICS
}
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.lyricsView.updateTime(progress.toLong())
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
loadLRCLyrics()
}
override fun onServiceConnected() {
super.onServiceConnected()
loadLRCLyrics()
}
override fun onResume() {
super.onResume()
updateHelper.start()
@ -415,14 +360,13 @@ class LyricsFragment : AbsMainActivityFragment(R.layout.fragment_lyrics) {
override fun onDestroyView() {
super.onDestroyView()
if (MusicPlayerRemote.playingQueue.isNotEmpty())
mainActivity.expandPanel()
_binding = null
}
}
override fun onDestroyView() {
super.onDestroyView()
if (MusicPlayerRemote.playingQueue.isNotEmpty())
(requireActivity() as MainActivity).expandPanel()
_binding = null
enum class LyricsType {
NORMAL_LYRICS,
SYNCED_LYRICS
}
}

View file

@ -11,6 +11,7 @@ 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.extensions.dipToPix
import code.name.monkey.retromusic.fragments.NowPlayingScreen
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
@ -21,6 +22,7 @@ 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 code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.jaudiotagger.audio.exceptions.CannotReadException
@ -56,6 +58,17 @@ class CoverLyricsFragment : AbsMusicServiceFragment(R.layout.fragment_cover_lyri
}
}
fun setColors(color: MediaNotificationProcessor) {
binding.run {
playerLyrics.background = null
playerLyricsLine1.setTextColor(color.primaryTextColor)
playerLyricsLine1.setShadowLayer(dipToPix(10f), 0f, 0f, color.backgroundColor)
playerLyricsLine2.setTextColor(color.primaryTextColor)
playerLyricsLine2.setShadowLayer(dipToPix(10f), 0f, 0f, color.backgroundColor)
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == SHOW_LYRICS) {
if (sharedPreferences?.getBoolean(key, false) == true) {

View file

@ -46,12 +46,12 @@ import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.transform.CarousalPagerTransformer
import code.name.monkey.retromusic.transform.ParallaxPagerTransformer
import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.LyricsType
import code.name.monkey.retromusic.util.CoverLyricsType
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import code.name.monkey.retromusic.util.logD
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover),
ViewPager.OnPageChangeListener, MusicProgressViewUpdateHelper.Callback,
@ -95,11 +95,13 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
if (embeddedLyrics != null) {
binding.lyricsView.loadLrc(embeddedLyrics)
} else {
withContext(Dispatchers.Main) {
binding.lyricsView.reset()
binding.lyricsView.setLabel(context?.getString(R.string.no_lyrics_found))
}
}
}
}
}
@ -171,13 +173,11 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
}
override fun onServiceConnected() {
logD("Service Connected")
updatePlayingQueue()
updateLyrics()
}
override fun onPlayingMetaChanged() {
logD("Playing Meta Changed")
if (viewPager.currentItem != MusicPlayerRemote.position) {
viewPager.setCurrentItem(MusicPlayerRemote.position, true)
}
@ -185,7 +185,6 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
}
override fun onQueueChanged() {
logD("Queue Changed")
updatePlayingQueue()
}
@ -219,7 +218,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
binding.coverLyrics.isVisible = false
binding.lyricsView.isVisible = false
binding.viewPager.isVisible = true
val lyrics: View = if (PreferenceUtil.lyricsType == LyricsType.REPLACE_COVER) {
val lyrics: View = if (PreferenceUtil.lyricsType == CoverLyricsType.REPLACE_COVER) {
ObjectAnimator.ofFloat(viewPager, View.ALPHA, if (visible) 0F else 1F).start()
lrcView
} else {
@ -239,7 +238,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
// Don't show lyrics container for below conditions
if (lyricViewNpsList.contains(nps) && PreferenceUtil.showLyrics) {
showLyrics(true)
if (PreferenceUtil.lyricsType == LyricsType.REPLACE_COVER) {
if (PreferenceUtil.lyricsType == CoverLyricsType.REPLACE_COVER) {
progressViewUpdateHelper?.start()
}
} else {
@ -250,8 +249,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
private fun updatePlayingQueue() {
binding.viewPager.apply {
adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue)
adapter?.notifyDataSetChanged()
adapter = AlbumCoverPagerAdapter(parentFragmentManager, MusicPlayerRemote.playingQueue)
setCurrentItem(MusicPlayerRemote.position, true)
onPageSelected(MusicPlayerRemote.position)
}
@ -260,7 +258,6 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
logD("Page Selected $position")
currentPosition = position
if (binding.viewPager.adapter != null) {
(binding.viewPager.adapter as AlbumCoverPagerAdapter).receiveColor(

View file

@ -74,8 +74,8 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
libraryViewModel.updateColor(color.backgroundColor)
ToolbarContentTintHelper.colorizeToolbar(binding.playerToolbar, Color.WHITE, activity)
binding.playerToolbar.setTitleTextColor(Color.WHITE)
binding.playerToolbar.setSubtitleTextColor(Color.WHITE)
binding.title.setTextColor(Color.WHITE)
binding.text.setTextColor(Color.WHITE)
}
override fun toggleFavorite(song: Song) {
@ -94,7 +94,7 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
_binding = FragmentCardBlurPlayerBinding.bind(view)
setUpSubFragments()
setUpPlayerToolbar()
binding.cardContainer?.drawAboveSystemBars()
binding.playerToolbar.drawAboveSystemBars()
}
private fun setUpSubFragments() {
@ -130,9 +130,9 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
private fun updateSong() {
val song = MusicPlayerRemote.currentSong
binding.playerToolbar.apply {
title = song.title
subtitle = song.artistName
binding.run {
title.text = song.title
text.text = song.artistName
}
}

View file

@ -28,6 +28,7 @@ import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.whichFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.fragments.player.CoverLyricsFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
@ -98,6 +99,7 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) {
controlsFragment.setColor(color)
libraryViewModel.updateColor(color.backgroundColor)
ToolbarContentTintHelper.colorizeToolbar(binding.playerToolbar, Color.WHITE, activity)
binding.coverLyrics.getFragment<CoverLyricsFragment>().setColors(color)
}
override fun onFavoriteToggled() {

View file

@ -44,6 +44,7 @@ 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.fragments.other.VolumeFragment
import code.name.monkey.retromusic.fragments.player.CoverLyricsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
@ -270,6 +271,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
updateRepeatState()
updateShuffleState()
updatePrevNextColor()
binding.coverLyrics.getFragment<CoverLyricsFragment>().setColors(color)
}
override fun onFavoriteToggled() {
@ -379,17 +381,22 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private fun setUpPlayPauseFab() {
binding.playbackControlsFragment.playPauseButton.setOnClickListener(
PlayPauseButtonOnClickHandler())
PlayPauseButtonOnClickHandler()
)
}
@SuppressLint("ClickableViewAccessibility")
private fun setUpPrevNext() {
updatePrevNextColor()
binding.playbackControlsFragment.nextButton.setOnTouchListener(MusicSeekSkipTouchListener(
binding.playbackControlsFragment.nextButton.setOnTouchListener(
MusicSeekSkipTouchListener(
requireActivity(),
true))
true
)
)
binding.playbackControlsFragment.previousButton.setOnTouchListener(
MusicSeekSkipTouchListener(requireActivity(), false))
MusicSeekSkipTouchListener(requireActivity(), false)
)
}
private fun updatePrevNextColor() {

View file

@ -23,6 +23,7 @@ import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
import code.name.monkey.retromusic.interfaces.ICabCallback
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.ThemedFastScroller
import com.afollestad.materialcab.attached.AttachedCab
@ -72,6 +73,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
binding.container.transitionName = "playlist"
playlist = arguments.extraPlaylist
binding.toolbar.title = playlist.playlistEntity.playlistName
binding.toolbar.subtitle =
MusicUtil.getPlaylistInfoString(requireContext(), playlist.songs.toSongs())
setUpRecyclerView()
viewModel.getSongs().observe(viewLifecycleOwner) {
songs(it.toSongs())

View file

@ -24,13 +24,13 @@ import code.name.monkey.retromusic.EXTRA_PLAYLIST
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter
import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.extensions.setUpMediaRouteButton
import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
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
class PlaylistsFragment :
@ -85,7 +85,7 @@ class PlaylistsFragment :
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
MenuCompat.setGroupDividerEnabled(menu, true)
//Setting up cast button
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
requireContext().setUpMediaRouteButton(menu)
}
override fun onMenuItemSelected(item: MenuItem): Boolean {

View file

@ -26,6 +26,7 @@ import androidx.preference.PreferenceManager
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.dip
import code.name.monkey.retromusic.extensions.goToProVersion
import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.preferences.*
import code.name.monkey.retromusic.util.NavigationUtil
@ -39,7 +40,7 @@ abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() {
internal fun showProToastAndNavigate(message: String) {
showToast(getString(R.string.message_pro_feature, message))
NavigationUtil.goToProVersion(requireActivity())
requireContext().goToProVersion()
}
internal fun setSummary(preference: Preference, value: Any?) {

View file

@ -27,6 +27,7 @@ import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentMainSettingsBinding
import code.name.monkey.retromusic.extensions.drawAboveSystemBarsWithPadding
import code.name.monkey.retromusic.extensions.goToProVersion
import code.name.monkey.retromusic.util.NavigationUtil
class MainSettingsFragment : Fragment(), View.OnClickListener {
@ -77,11 +78,11 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
binding.buyProContainer.apply {
isGone = App.isProVersion()
setOnClickListener {
NavigationUtil.goToProVersion(requireContext())
requireContext().goToProVersion()
}
}
binding.buyPremium.setOnClickListener {
NavigationUtil.goToProVersion(requireContext())
requireContext().goToProVersion()
}
ThemeStore.accentColor(requireContext()).let {
binding.buyPremium.setTextColor(it)

View file

@ -21,12 +21,10 @@ import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference
import code.name.monkey.retromusic.LANGUAGE_NAME
import code.name.monkey.retromusic.LAST_ADDED_CUTOFF
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.installLanguageAndRecreate
import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType.HomeSections
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory
import com.google.android.play.core.splitinstall.SplitInstallRequest
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.util.*
/**
* @author Hemanth S (h4h13).
@ -58,21 +56,7 @@ class OtherSettingsFragment : AbsSettingsFragment() {
val languagePreference: Preference? = findPreference(LANGUAGE_NAME)
languagePreference?.setOnPreferenceChangeListener { prefs, newValue ->
setSummary(prefs, newValue)
val code = newValue.toString()
val manager = SplitInstallManagerFactory.create(requireContext())
if (code != "auto") {
// Try to download language resources
val request =
SplitInstallRequest.newBuilder().addLanguage(Locale.forLanguageTag(code))
.build()
manager.startInstall(request)
// Recreate the activity on download complete
.addOnCompleteListener {
restartActivity()
}
} else {
requireActivity().recreate()
}
requireActivity().installLanguageAndRecreate(newValue.toString())
true
}
}

View file

@ -21,6 +21,7 @@ import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.GridLayoutManager
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.extensions.setUpMediaRouteButton
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType
@ -35,7 +36,6 @@ import com.afollestad.materialcab.attached.AttachedCab
import com.afollestad.materialcab.attached.destroy
import com.afollestad.materialcab.attached.isActive
import com.afollestad.materialcab.createCab
import com.google.android.gms.cast.framework.CastButtonFactory
class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>(),
ICabHolder {
@ -136,7 +136,7 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
setupLayoutMenu(layoutItem.subMenu)
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
//Setting up cast button
CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.action_cast)
requireContext().setUpMediaRouteButton(menu)
}
private fun setUpSortOrderMenu(sortOrderMenu: SubMenu) {

View file

@ -27,10 +27,10 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.SongRepository
import code.name.monkey.retromusic.service.CastPlayer
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.getExternalStorageDirectory
import code.name.monkey.retromusic.util.logE
import com.google.android.gms.cast.framework.CastSession
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.File
@ -171,9 +171,6 @@ object MusicPlayerRemote : KoinComponent {
return musicService?.playingQueue?.size ?: -1
}
/**
* Async
*/
fun playSongAt(position: Int) {
musicService?.playSongAt(position)
}
@ -456,8 +453,8 @@ object MusicPlayerRemote : KoinComponent {
.dropLastWhile { it.isEmpty() }.toTypedArray()[1]
}
fun switchToRemotePlayback(castSession: CastSession) {
musicService?.switchToRemotePlayback(castSession)
fun switchToRemotePlayback(castPlayer: CastPlayer) {
musicService?.switchToRemotePlayback(castPlayer)
}
fun switchToLocalPlayback() {

View file

@ -19,7 +19,6 @@ 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
@ -35,14 +34,15 @@ import android.widget.Scroller
import androidx.core.content.ContextCompat
import androidx.core.graphics.withSave
import code.name.monkey.retromusic.R
import kotlinx.coroutines.*
import java.io.File
import java.lang.Runnable
import kotlin.math.abs
/**
* 歌词 Created by wcy on 2015/11/9.
*/
@SuppressLint("StaticFieldLeak")
@Suppress("deprecation")
class CoverLrcView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
@ -72,7 +72,6 @@ class CoverLrcView @JvmOverloads constructor(
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
@ -85,9 +84,8 @@ class CoverLrcView @JvmOverloads constructor(
}
}
/**
* 手势监听器
*/
private val viewScope = CoroutineScope(Dispatchers.Main + Job())
private val mSimpleOnGestureListener: SimpleOnGestureListener =
object : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
@ -251,42 +249,31 @@ class CoverLrcView @JvmOverloads constructor(
mScroller = Scroller(context)
}
/** 设置非当前行歌词字体颜色 */
fun setNormalColor(normalColor: Int) {
mNormalTextColor = normalColor
postInvalidate()
}
/** 设置当前行歌词的字体颜色 */
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" }
@ -296,17 +283,6 @@ class CoverLrcView @JvmOverloads constructor(
}
}
/**
* 设置播放按钮点击监听器
*
* @param onPlayClickListener 如果为非 null 则激活歌词拖动功能否则将将禁用歌词拖动功能
*/
@Deprecated("use {@link #setDraggable(boolean, OnPlayClickListener)} instead")
fun setOnPlayClickListener(onPlayClickListener: OnPlayClickListener?) {
mOnPlayClickListener = onPlayClickListener
}
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
fun setLabel(label: String?) {
runOnUi {
mDefaultLabel = label
@ -314,106 +290,40 @@ class CoverLrcView @JvmOverloads constructor(
}
}
/**
* 加载歌词文件
*
* @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) {
viewScope.launch(Dispatchers.IO) {
val lrcEntries = LrcUtils.parseLrc(arrayOf(lrcFile, null))
withContext(Dispatchers.Main) {
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) {
viewScope.launch(Dispatchers.IO) {
val lrcEntries = LrcUtils.parseLrc(arrayOf(lrcText, null))
withContext(Dispatchers.Main) {
onLrcLoaded(lrcEntries)
this@CoverLrcView.flag = null
}
}
}.execute(mainLrcText, secondLrcText)
}
}
/**
* 歌词是否有效
*
* @return true如果歌词有效否则false
*/
fun hasLrc(): Boolean {
return mLrcEntryList.isNotEmpty()
}
/**
* 刷新歌词
*
* @param time 当前播放时间
*/
fun updateTime(time: Long) {
runOnUi {
if (!hasLrc()) {
return@runOnUi
}
val line = findShowLine(time)
val line = findShowLine(time + 300L)
if (line != mCurrentLine) {
mCurrentLine = line
if (!isShowTimeline) {
@ -441,9 +351,9 @@ class CoverLrcView @JvmOverloads constructor(
super.onDraw(canvas)
val centerY = height / 2
// 无歌词文件
if (!hasLrc()) {
mLrcPaint.color = mCurrentTextColor
@Suppress("Deprecation")
@SuppressLint("DrawAllocation") val staticLayout = StaticLayout(
mDefaultLabel,
mLrcPaint,
@ -485,11 +395,6 @@ class CoverLrcView @JvmOverloads constructor(
}
}
/**
* 画一行歌词
*
* @param y 歌词中心 Y 坐标
*/
private fun drawText(canvas: Canvas, staticLayout: StaticLayout, y: Float) {
canvas.withSave {
translate(mLrcPadding, y - (staticLayout.height shr 1))
@ -539,6 +444,7 @@ class CoverLrcView @JvmOverloads constructor(
override fun onDetachedFromWindow() {
removeCallbacks(hideTimelineRunnable)
viewScope.cancel()
super.onDetachedFromWindow()
}
@ -582,12 +488,10 @@ class CoverLrcView @JvmOverloads constructor(
invalidate()
}
/** 将中心行微调至正中心 */
private fun adjustCenter() {
smoothScrollTo(centerLine, ADJUST_DURATION)
}
/** 滚动到某一行 */
private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) {
val offset = getOffset(line)
endAnimation()
@ -602,14 +506,12 @@ class CoverLrcView @JvmOverloads constructor(
}
}
/** 结束滚动动画 */
private fun endAnimation() {
if (mAnimator != null && mAnimator!!.isRunning) {
mAnimator!!.end()
}
}
/** 二分法查找当前时间应该显示的行数(最后一个 <= time 的行数) */
private fun findShowLine(time: Long): Int {
var left = 0
var right = mLrcEntryList.size
@ -628,7 +530,6 @@ class CoverLrcView @JvmOverloads constructor(
return 0
}
/** 获取当前在视图中央的行数 */
private val centerLine: Int
get() {
var centerLine = 0
@ -642,7 +543,6 @@ class CoverLrcView @JvmOverloads constructor(
return centerLine
}
/** 获取歌词距离视图顶部的距离 采用懒加载方式 */
private fun getOffset(line: Int): Float {
if (mLrcEntryList.isEmpty()) return 0F
if (mLrcEntryList[line].offset == Float.MIN_VALUE) {
@ -656,11 +556,9 @@ class CoverLrcView @JvmOverloads constructor(
return mLrcEntryList[line].offset
}
/** 获取歌词宽度 */
private val lrcWidth: Float
get() = width - mLrcPadding * 2
/** 在主线程中运行 */
private fun runOnUi(r: Runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
r.run()
@ -669,13 +567,7 @@ class CoverLrcView @JvmOverloads constructor(
}
}
/** 播放按钮点击监听器,点击后应该跳转到指定播放位置 */
fun interface OnPlayClickListener {
/**
* 播放按钮被点击应该跳转到指定播放位置
*
* @return 是否成功消费该事件如果成功消费则会更新UI
*/
fun onPlayClick(time: Long): Boolean
}

View file

@ -35,6 +35,8 @@ import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@ -90,9 +92,7 @@ public class LrcView extends View {
}
}
};
/**
* 手势监听器
*/
private final GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
@ -111,7 +111,7 @@ public class LrcView extends View {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (hasLrc()) {
mOffset += -distanceY;
mOffset -= distanceY;
mOffset = Math.min(mOffset, getOffset(0));
mOffset = Math.max(mOffset, getOffset(mLrcEntryList.size() - 1));
invalidate();
@ -219,7 +219,7 @@ public class LrcView extends View {
mPlayDrawable = ta.getDrawable(R.styleable.LrcView_lrcPlayDrawable);
mPlayDrawable =
(mPlayDrawable == null)
? getResources().getDrawable(R.drawable.ic_play_arrow)
? ContextCompat.getDrawable(getContext(), R.drawable.ic_play_arrow)
: mPlayDrawable;
mTimeTextColor =
ta.getColor(
@ -252,52 +252,28 @@ public class LrcView extends View {
mScroller = new Scroller(getContext());
}
/** 设置非当前行歌词字体颜色 */
public void setNormalColor(int normalColor) {
mNormalTextColor = normalColor;
postInvalidate();
}
/** 普通歌词文本字体大小 */
public void setNormalTextSize(float size) {
mNormalTextSize = size;
}
/** 当前歌词文本字体大小 */
public void setCurrentTextSize(float size) {
mCurrentTextSize = size;
}
/** 设置当前行歌词的字体颜色 */
public void setCurrentColor(int currentColor) {
mCurrentTextColor = currentColor;
postInvalidate();
}
/** 设置拖动歌词时选中歌词的字体颜色 */
public void setTimelineTextColor(int timelineTextColor) {
mTimelineTextColor = timelineTextColor;
postInvalidate();
}
/** 设置拖动歌词时时间线的颜色 */
public void setTimelineColor(int timelineColor) {
mTimelineColor = timelineColor;
postInvalidate();
}
/** 设置拖动歌词时右侧时间字体颜色 */
public void setTimeTextColor(int timeTextColor) {
mTimeTextColor = timeTextColor;
postInvalidate();
}
/**
* 设置歌词是否允许拖动
*
* @param draggable 是否允许拖动
* @param onPlayClickListener 设置歌词拖动后播放按钮点击监听器如果允许拖动则不能为 null
*/
public void setDraggable(boolean draggable, OnPlayClickListener onPlayClickListener) {
if (draggable) {
if (onPlayClickListener == null) {
@ -310,18 +286,6 @@ public class LrcView extends View {
}
}
/**
* 设置播放按钮点击监听器
*
* @param onPlayClickListener 如果为非 null 则激活歌词拖动功能否则将将禁用歌词拖动功能
* @deprecated use {@link #setDraggable(boolean, OnPlayClickListener)} instead
*/
@Deprecated
public void setOnPlayClickListener(OnPlayClickListener onPlayClickListener) {
mOnPlayClickListener = onPlayClickListener;
}
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
public void setLabel(String label) {
runOnUi(
() -> {
@ -330,24 +294,12 @@ public class LrcView extends View {
});
}
/**
* 加载歌词文件
*
* @param lrcFile 歌词文件
*/
public void loadLrc(File lrcFile) {
loadLrc(lrcFile, null);
}
/**
* 加载双语歌词文件两种语言的歌词时间戳需要一致
*
* @param mainLrcFile 第一种语言歌词文件
* @param secondLrcFile 第二种语言歌词文件
*/
public void loadLrc(File mainLrcFile, File secondLrcFile) {
runOnUi(
() -> {
runOnUi(() -> {
reset();
StringBuilder sb = new StringBuilder("file://");
@ -374,21 +326,10 @@ public class LrcView extends View {
});
}
/**
* 加载歌词文本
*
* @param lrcText 歌词文本
*/
public void loadLrc(String lrcText) {
loadLrc(lrcText, null);
}
/**
* 加载双语歌词文本两种语言的歌词时间戳需要一致
*
* @param mainLrcText 第一种语言歌词文本
* @param secondLrcText 第二种语言歌词文本
*/
public void loadLrc(String mainLrcText, String secondLrcText) {
runOnUi(
() -> {
@ -418,53 +359,10 @@ public class LrcView extends View {
});
}
/**
* 加载在线歌词默认使用 utf-8 编码
*
* @param lrcUrl 歌词文件的网络地址
*/
public void loadLrcByUrl(String lrcUrl) {
loadLrcByUrl(lrcUrl, "utf-8");
}
/**
* 加载在线歌词
*
* @param lrcUrl 歌词文件的网络地址
* @param charset 编码格式
*/
public void loadLrcByUrl(String lrcUrl, String charset) {
String flag = "url://" + lrcUrl;
setFlag(flag);
new AsyncTask<String, Integer, String>() {
@Override
protected String doInBackground(String... params) {
return LrcUtils.getContentFromNetwork(params[0], params[1]);
}
@Override
protected void onPostExecute(String lrcText) {
if (getFlag() == flag) {
loadLrc(lrcText);
}
}
}.execute(lrcUrl, charset);
}
/**
* 歌词是否有效
*
* @return true如果歌词有效否则false
*/
public boolean hasLrc() {
return !mLrcEntryList.isEmpty();
}
/**
* 刷新歌词
*
* @param time 当前播放时间
*/
public void updateTime(long time) {
runOnUi(
() -> {
@ -484,12 +382,6 @@ public class LrcView extends View {
});
}
/**
* 将歌词滚动到指定时间
*
* @param time 指定的时间
* @deprecated 请使用 {@link #updateTime(long)} 代替
*/
@Deprecated
public void onDrag(long time) {
updateTime(time);
@ -513,7 +405,6 @@ public class LrcView extends View {
int centerY = getHeight() / 2;
// 无歌词文件
if (!hasLrc()) {
mLrcPaint.setColor(mCurrentTextColor);
@SuppressLint("DrawAllocation")
@ -570,11 +461,6 @@ public class LrcView extends View {
}
}
/**
* 画一行歌词
*
* @param y 歌词中心 Y 坐标
*/
private void drawText(Canvas canvas, StaticLayout staticLayout, float y) {
canvas.save();
canvas.translate(mLrcPadding, y - (staticLayout.getHeight() >> 1));
@ -661,17 +547,14 @@ public class LrcView extends View {
invalidate();
}
/** 将中心行微调至正中心 */
private void adjustCenter() {
smoothScrollTo(getCenterLine(), ADJUST_DURATION);
}
/** 滚动到某一行 */
private void smoothScrollTo(int line) {
smoothScrollTo(line, mAnimationDuration);
}
/** 滚动到某一行 */
private void smoothScrollTo(int line, long duration) {
float offset = getOffset(line);
endAnimation();
@ -687,14 +570,12 @@ public class LrcView extends View {
mAnimator.start();
}
/** 结束滚动动画 */
private void endAnimation() {
if (mAnimator != null && mAnimator.isRunning()) {
mAnimator.end();
}
}
/** 二分法查找当前时间应该显示的行数(最后一个 <= time 的行数) */
private int findShowLine(long time) {
int left = 0;
int right = mLrcEntryList.size();
@ -716,7 +597,6 @@ public class LrcView extends View {
return 0;
}
/** 获取当前在视图中央的行数 */
private int getCenterLine() {
int centerLine = 0;
float minDistance = Float.MAX_VALUE;
@ -729,7 +609,6 @@ public class LrcView extends View {
return centerLine;
}
/** 获取歌词距离视图顶部的距离 采用懒加载方式 */
private float getOffset(int line) {
if (mLrcEntryList.get(line).getOffset() == Float.MIN_VALUE) {
float offset = getHeight() / 2;
@ -744,12 +623,10 @@ public class LrcView extends View {
return mLrcEntryList.get(line).getOffset();
}
/** 获取歌词宽度 */
private float getLrcWidth() {
return getWidth() - mLrcPadding * 2;
}
/** 在主线程中运行 */
private void runOnUi(Runnable r) {
if (Looper.myLooper() == Looper.getMainLooper()) {
r.run();
@ -766,13 +643,7 @@ public class LrcView extends View {
this.mFlag = flag;
}
/** 播放按钮点击监听器,点击后应该跳转到指定播放位置 */
public interface OnPlayClickListener {
/**
* 播放按钮被点击应该跳转到指定播放位置
*
* @return 是否成功消费该事件如果成功消费则会更新UI
*/
boolean onPlayClick(long time);
}
}

View file

@ -81,7 +81,7 @@ class AlbumCoverStylePreferenceDialog : DialogFragment(),
if (isAlbumCoverStyle(coverStyle)) {
val result = getString(coverStyle.titleRes) + " theme is Pro version feature."
showToast(result)
NavigationUtil.goToProVersion(requireActivity())
requireContext().goToProVersion()
} else {
PreferenceUtil.albumCoverStyle = coverStyle
}

View file

@ -91,7 +91,7 @@ class NowPlayingScreenPreferenceDialog : DialogFragment(), ViewPager.OnPageChang
val result =
"${getString(nowPlayingScreen.titleRes)} theme is Pro version feature."
showToast(result)
NavigationUtil.goToProVersion(requireContext())
requireContext().goToProVersion()
} else {
PreferenceUtil.nowPlayingScreen = nowPlayingScreen
}

View file

@ -333,6 +333,10 @@ internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatche
fun MediaPlayer.setPlaybackSpeedPitch(speed: Float, pitch: Float) {
if (hasMarshmallow()) {
val wasPlaying = isPlaying
playbackParams = PlaybackParams().setSpeed(speed).setPitch(pitch)
if (!wasPlaying) {
pause()
}
}
}

View file

@ -15,10 +15,10 @@
package code.name.monkey.retromusic.service
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.os.PowerManager
import android.os.PowerManager.WakeLock
@ -62,8 +62,7 @@ class MediaButtonIntentReceiver : MediaButtonReceiver() {
private var mClickCounter = 0
private var mLastClickTime: Long = 0
@SuppressLint("HandlerLeak") // false alarm, handler is already static
private val mHandler = object : Handler() {
private val mHandler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {

View file

@ -86,7 +86,6 @@ import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import com.google.android.gms.cast.framework.CastSession
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Default
import kotlinx.coroutines.Dispatchers.IO
@ -286,12 +285,16 @@ class MusicService : MediaBrowserServiceCompat(),
initNotification()
mediaStoreObserver = MediaStoreObserver(this, playerHandler!!)
throttledSeekHandler = ThrottledSeekHandler(this, Handler(mainLooper))
contentResolver.registerContentObserver(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
contentResolver.registerContentObserver(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
true,
mediaStoreObserver)
contentResolver.registerContentObserver(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,
mediaStoreObserver
)
contentResolver.registerContentObserver(
MediaStore.Audio.Media.INTERNAL_CONTENT_URI,
true,
mediaStoreObserver)
mediaStoreObserver
)
val audioVolumeObserver = AudioVolumeObserver(this)
audioVolumeObserver.register(AudioManager.STREAM_MUSIC, this)
registerOnSharedPreferenceChangedListener(this)
@ -319,6 +322,7 @@ class MusicService : MediaBrowserServiceCompat(),
mediaSession?.isActive = false
quit()
releaseResources()
serviceScope.cancel()
contentResolver.unregisterContentObserver(mediaStoreObserver)
unregisterOnSharedPreferenceChangedListener(this)
wakeLock?.release()
@ -1014,8 +1018,10 @@ class MusicService : MediaBrowserServiceCompat(),
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName)
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title)
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration)
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER,
(getPosition() + 1).toLong())
.putLong(
MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER,
(getPosition() + 1).toLong()
)
.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.year.toLong())
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null)
.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, playingQueue.size.toLong())
@ -1171,8 +1177,8 @@ class MusicService : MediaBrowserServiceCompat(),
playbackManager.switchToLocalPlayback(this::restorePlaybackState)
}
fun switchToRemotePlayback(castSession: CastSession) {
playbackManager.switchToRemotePlayback(castSession, this::restorePlaybackState)
fun switchToRemotePlayback(castPlayer: CastPlayer) {
playbackManager.switchToRemotePlayback(castPlayer, this::restorePlaybackState)
}
private fun restorePlaybackState(wasPlaying: Boolean, progress: Int) {
@ -1304,8 +1310,10 @@ class MusicService : MediaBrowserServiceCompat(),
}
private fun setupMediaSession() {
val mediaButtonReceiverComponentName = ComponentName(applicationContext,
MediaButtonIntentReceiver::class.java)
val mediaButtonReceiverComponentName = ComponentName(
applicationContext,
MediaButtonIntentReceiver::class.java
)
val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON)
mediaButtonIntent.component = mediaButtonReceiverComponentName

View file

@ -8,7 +8,6 @@ import code.name.monkey.retromusic.service.playback.Playback
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.PreferenceUtil.playbackPitch
import code.name.monkey.retromusic.util.PreferenceUtil.playbackSpeed
import com.google.android.gms.cast.framework.CastSession
class PlaybackManager(val context: Context) {
@ -160,11 +159,11 @@ class PlaybackManager(val context: Context) {
}
fun switchToRemotePlayback(
castSession: CastSession,
castPlayer: CastPlayer,
onChange: (wasPlaying: Boolean, progress: Int) -> Unit,
) {
playbackLocation = PlaybackLocation.REMOTE
switchToPlayback(CastPlayer(castSession), onChange)
switchToPlayback(castPlayer, onChange)
}
private fun switchToPlayback(

View file

@ -22,7 +22,6 @@ import androidx.viewpager.widget.ViewPager
*/
class ParallaxPagerTransformer(private val id: Int) : ViewPager.PageTransformer {
private var border = 0
private var speed = 0.2f
override fun transformPage(page: View, position: Float) {
@ -32,22 +31,12 @@ class ParallaxPagerTransformer(private val id: Int) : ViewPager.PageTransformer
if (position > -1 && position < 1) {
val width = parallaxView.width.toFloat()
parallaxView.translationX = -(position * width * speed)
val sc = (width - border) / width
if (position == 0f) {
scaleX = 1f
scaleY = 1f
} else {
scaleX = sc
scaleY = sc
}
}
}
}
}
fun setBorder(px: Int) {
border = px
}
fun setSpeed(speed: Float) {
this.speed = speed

View file

@ -117,7 +117,7 @@ object LyricUtil {
return "$lrcRootPath$title - $artist.lrc"
}
fun getLrcOriginalPath(filePath: String): String {
private fun getLrcOriginalPath(filePath: String): String {
return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1), "lrc")
}

View file

@ -39,12 +39,6 @@ object NavigationUtil {
)
}
fun goToProVersion(context: Context) {
context.startActivity(
Intent(context, PurchaseActivity::class.java), null
)
}
fun goToSupportDevelopment(activity: Activity) {
activity.startActivity(
Intent(activity, SupportDevelopmentActivity::class.java), null

View file

@ -115,7 +115,7 @@ object PreferenceUtil {
putString(SAF_SDCARD_URI, value)
}
val autoDownloadImagesPolicy
private val autoDownloadImagesPolicy
get() = sharedPreferences.getStringOrDefault(
AUTO_DOWNLOAD_IMAGES_POLICY,
"only_wifi"
@ -242,8 +242,6 @@ object PreferenceUtil {
val isScreenOnEnabled get() = sharedPreferences.getBoolean(KEEP_SCREEN_ON, false)
val isShuffleModeOn get() = sharedPreferences.getBoolean(TOGGLE_SHUFFLE, false)
val isSongInfo get() = sharedPreferences.getBoolean(EXTRA_SONG_INFO, false)
val isPauseOnZeroVolume get() = sharedPreferences.getBoolean(PAUSE_ON_ZERO_VOLUME, false)
@ -472,10 +470,10 @@ object PreferenceUtil {
val tabTitleMode: Int
get() {
return when (sharedPreferences.getStringOrDefault(
TAB_TEXT_MODE, "1"
TAB_TEXT_MODE, "0"
).toInt()) {
1 -> BottomNavigationView.LABEL_VISIBILITY_LABELED
0 -> BottomNavigationView.LABEL_VISIBILITY_AUTO
1 -> BottomNavigationView.LABEL_VISIBILITY_LABELED
2 -> BottomNavigationView.LABEL_VISIBILITY_SELECTED
3 -> BottomNavigationView.LABEL_VISIBILITY_UNLABELED
else -> BottomNavigationView.LABEL_VISIBILITY_LABELED
@ -697,11 +695,11 @@ object PreferenceUtil {
val isSnowFalling
get() = sharedPreferences.getBoolean(SNOWFALL, false)
val lyricsType: LyricsType
val lyricsType: CoverLyricsType
get() = if (sharedPreferences.getString(LYRICS_TYPE, "0") == "0") {
LyricsType.REPLACE_COVER
CoverLyricsType.REPLACE_COVER
} else {
LyricsType.OVER_COVER
CoverLyricsType.OVER_COVER
}
var playbackSpeed
@ -740,6 +738,6 @@ object PreferenceUtil {
get() = sharedPreferences.getBoolean(SWIPE_DOWN_DISMISS, true)
}
enum class LyricsType {
enum class CoverLyricsType {
REPLACE_COVER, OVER_COVER
}

View file

@ -1,33 +0,0 @@
package code.name.monkey.retromusic.util
import android.content.Context
import android.content.Intent
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.activities.PurchaseActivity
object PremiumShow {
private const val PREF_NAME = "premium_show"
private const val LAUNCH_COUNT = "launch_count"
private const val DATE_FIRST_LAUNCH = "date_first_launch"
@JvmStatic
fun launch(context: Context) {
val pref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
if (App.isProVersion()) {
return
}
val prefEditor = pref.edit()
val launchCount = pref.getLong(LAUNCH_COUNT, 0) + 1
prefEditor.putLong(LAUNCH_COUNT, launchCount)
var dateLaunched = pref.getLong(DATE_FIRST_LAUNCH, 0)
if (dateLaunched == 0L) {
dateLaunched = System.currentTimeMillis()
prefEditor.putLong(DATE_FIRST_LAUNCH, dateLaunched)
}
if (System.currentTimeMillis() >= dateLaunched + 2 * 24 * 60 * 60 * 1000) {
context.startActivity(Intent(context, PurchaseActivity::class.java), null)
}
prefEditor.apply()
}
}

View file

@ -19,14 +19,13 @@ import android.content.res.ColorStateList
import android.util.AttributeSet
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.NavigationViewUtil
import code.name.monkey.retromusic.extensions.addAlpha
import code.name.monkey.retromusic.extensions.setItemColors
import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
import dev.chrisbanes.insetter.applyInsetter
class BottomNavigationBarTinted @JvmOverloads constructor(
class TintedBottomNavigationView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
@ -55,16 +54,7 @@ class BottomNavigationBarTinted @JvmOverloads constructor(
if (!PreferenceUtil.materialYou) {
val iconColor = ATHUtil.resolveColor(context, android.R.attr.colorControlNormal)
val accentColor = ThemeStore.accentColor(context)
NavigationViewUtil.setItemIconColors(
this,
ColorUtil.withAlpha(iconColor, 0.5f),
accentColor
)
NavigationViewUtil.setItemTextColors(
this,
ColorUtil.withAlpha(iconColor, 0.5f),
accentColor
)
setItemColors(iconColor, accentColor)
itemRippleColor = ColorStateList.valueOf(accentColor.addAlpha(0.08F))
itemActiveIndicatorColor = ColorStateList.valueOf(accentColor.addAlpha(0.12F))
}

View file

@ -0,0 +1,46 @@
/*
* 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.views
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.addAlpha
import code.name.monkey.retromusic.extensions.setItemColors
import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.material.navigationrail.NavigationRailView
class TintedNavigationRailView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : NavigationRailView(context, attrs, defStyleAttr) {
init {
if (!isInEditMode) {
labelVisibilityMode = PreferenceUtil.tabTitleMode
if (!PreferenceUtil.materialYou) {
val iconColor = ATHUtil.resolveColor(context, android.R.attr.colorControlNormal)
val accentColor = context.accentColor()
setItemColors(iconColor, accentColor)
itemRippleColor = ColorStateList.valueOf(accentColor.addAlpha(0.08F))
itemActiveIndicatorColor = ColorStateList.valueOf(accentColor.addAlpha(0.12F))
}
}
}
}

View file

@ -5,6 +5,5 @@
android:viewportHeight="24">
<path
android:fillColor="#fff"
android:fillType="evenOdd"
android:pathData="M5,3C3.8954,3 3,3.8954 3,5V19C3,20.1046 3.8954,21 5,21H19C20.1046,21 21,20.1046 21,19V5C21,3.8954 20.1046,3 19,3H5ZM6,7C6,6.4477 6.4477,6 7,6H17C17.5523,6 18,6.4477 18,7C18,7.5523 17.5523,8 17,8H7C6.4477,8 6,7.5523 6,7ZM6,12C6,11.4477 6.4477,11 7,11H17C17.5523,11 18,11.4477 18,12C18,12.5523 17.5523,13 17,13H7C6.4477,13 6,12.5523 6,12ZM7,16C6.4477,16 6,16.4477 6,17C6,17.5523 6.4477,18 7,18H11C11.5523,18 12,17.5523 12,17C12,16.4477 11.5523,16 11,16H7Z" />
android:pathData="M2,22V4Q2,3.175 2.588,2.587Q3.175,2 4,2H15Q15.825,2 16.413,2.587Q17,3.175 17,4V4.425Q15.625,5.025 14.812,6.262Q14,7.5 14,9Q14,10.5 14.812,11.738Q15.625,12.975 17,13.575V16Q17,16.825 16.413,17.413Q15.825,18 15,18H6ZM6,14H10V12H6ZM19,12Q17.75,12 16.875,11.125Q16,10.25 16,9Q16,7.725 16.875,6.862Q17.75,6 19,6Q19.275,6 19.525,6.05Q19.775,6.1 20,6.175V1H24V3H22V9Q22,10.25 21.125,11.125Q20.25,12 19,12ZM6,11H13V9H6ZM6,8H13V6H6Z"/>
</vector>

View file

@ -4,17 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,3L19,3A2,2 0,0 1,21 5L21,19A2,2 0,0 1,19 21L5,21A2,2 0,0 1,3 19L3,5A2,2 0,0 1,5 3z"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#fff"/>
<path
android:pathData="M7,6L17,6A1,1 0,0 1,18 7L18,7A1,1 0,0 1,17 8L7,8A1,1 0,0 1,6 7L6,7A1,1 0,0 1,7 6z"
android:fillColor="#fff"/>
<path
android:pathData="M7,11L17,11A1,1 0,0 1,18 12L18,12A1,1 0,0 1,17 13L7,13A1,1 0,0 1,6 12L6,12A1,1 0,0 1,7 11z"
android:fillColor="#fff"/>
<path
android:pathData="M7,16L11,16A1,1 0,0 1,12 17L12,17A1,1 0,0 1,11 18L7,18A1,1 0,0 1,6 17L6,17A1,1 0,0 1,7 16z"
android:fillColor="#fff"/>
android:fillColor="#fff"
android:pathData="M2,22V4Q2,3.175 2.588,2.587Q3.175,2 4,2H15Q15.825,2 16.413,2.587Q17,3.175 17,4V4.425Q16.4,4.7 15.9,5.1Q15.4,5.5 15,6V4Q15,4 15,4Q15,4 15,4H4Q4,4 4,4Q4,4 4,4V16.175L4.175,16H15Q15,16 15,16Q15,16 15,16V12Q15.4,12.5 15.9,12.9Q16.4,13.3 17,13.575V16Q17,16.825 16.413,17.413Q15.825,18 15,18H6ZM6,14H10V12H6ZM19,12Q17.75,12 16.875,11.125Q16,10.25 16,9Q16,7.75 16.875,6.875Q17.75,6 19,6Q19.275,6 19.525,6.05Q19.775,6.1 20,6.175V1H24V3H22V9Q22,10.25 21.125,11.125Q20.25,12 19,12ZM6,11H13V9H6ZM6,8H13V6H6ZM4,16.175V4Q4,4 4,4Q4,4 4,4Q4,4 4,4Q4,4 4,4V6Q4,6.625 4,7.387Q4,8.15 4,9Q4,9.85 4,10.613Q4,11.375 4,12V16Q4,16 4,16Q4,16 4,16Z" />
</vector>

View file

@ -4,7 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorSurface">
android:background="?colorSurface"
android:clickable="true"
android:focusable="true">
<include layout="@layout/shadow_statusbar_toolbar" />

View file

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/colorBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:srcCompat="@color/black_color" />
<View
android:id="@+id/mask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/shadow_up_strong" />
<include layout="@layout/shadow_statusbar_toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/status_bar" />
</FrameLayout>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/playerToolbar"
style="@style/Toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp"
app:contentInsetStartWithNavigation="0dp"
app:navigationIcon="@drawable/ic_keyboard_arrow_down_black"
app:subtitleTextAppearance="@style/TextAppearance.AppCompat.Caption"
app:titleMargin="0dp"
app:titleMarginStart="0dp"
app:titleTextAppearance="@style/TextAppearance.AppCompat.Subhead" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="64dp"
android:layout_marginEnd="64dp"
android:layout_weight="1">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playerAlbumCoverFragment"
android:name="code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="@layout/fragment_album_card_cover" />
</FrameLayout>
</LinearLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playbackControlsFragment"
android:name="code.name.monkey.retromusic.fragments.player.cardblur.CardBlurPlaybackControlsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="96dp"
android:layout_marginEnd="96dp"
tools:layout="@layout/fragment_card_blur_player_playback_controls" />
</FrameLayout>

View file

@ -10,7 +10,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.navigationrail.NavigationRailView
<code.name.monkey.retromusic.views.TintedNavigationRailView
android:id="@+id/navigationView"
style="@style/Widget.Material3.NavigationRailView"
android:layout_width="wrap_content"

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
@ -31,52 +30,5 @@
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/donation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="@string/donation_header"
android:textAppearance="@style/TextViewSubtitle2"
android:textColor="?attr/colorAccent" />
<LinearLayout
android:id="@+id/progressContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="12dp">
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:paddingLeft="16dp"
android:text="@string/loading_products"
android:textAppearance="@style/TextViewCaption"
tools:ignore="RtlHardcoded,RtlSymmetry" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:scrollbarStyle="outsideOverlay" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -4,7 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorSurface">
android:background="?colorSurface"
android:clickable="true"
android:focusable="true">
<include layout="@layout/shadow_statusbar_toolbar" />

View file

@ -25,45 +25,58 @@
<include layout="@layout/shadow_statusbar_toolbar" />
<FrameLayout
android:id="@+id/cardContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/status_bar" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/playerToolbar"
style="@style/Toolbar"
android:layout_width="match_parent"
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp"
app:contentInsetStartWithNavigation="0dp"
app:navigationIcon="@drawable/ic_keyboard_arrow_down_black"
app:subtitleTextAppearance="@style/TextAppearance.AppCompat.Caption"
app:titleMargin="0dp"
app:titleMarginStart="0dp"
app:titleTextAppearance="@style/TextAppearance.AppCompat.Subhead" />
android:layout_marginTop="16dp"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:freezesText="true"
android:paddingHorizontal="24dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textAppearance="@style/TextViewHeadline6"
android:textColor="?android:attr/textColorPrimary"
android:textStyle="bold"
tools:text="@tools:sample/lorem" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:freezesText="true"
android:paddingHorizontal="24dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textAppearance="@style/TextViewBody1"
android:textColor="?android:attr/textColorPrimary"
tools:text="@tools:sample/lorem" />
<FrameLayout
android:id="@+id/cardContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playerAlbumCoverFragment"
android:name="code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="@layout/fragment_album_card_cover" />
android:layout_height="match_parent"
tools:layout="@layout/fragment_album_full_card_cover" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/cover_lyrics"
@ -72,9 +85,6 @@
android:layout_height="match_parent"
android:layout_gravity="center"
tools:layout="@layout/fragment_cover_lyrics" />
</FrameLayout>
</LinearLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playbackControlsFragment"
@ -85,4 +95,13 @@
tools:layout="@layout/fragment_card_blur_player_playback_controls" />
</FrameLayout>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/playerToolbar"
style="@style/Toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:navigationIcon="@drawable/ic_keyboard_arrow_down_black" />
</LinearLayout>
</FrameLayout>

View file

@ -21,7 +21,7 @@
android:layout_gravity="center_vertical"
android:gravity="center"
android:shadowColor="@color/md_black_1000"
android:shadowRadius="4"
android:shadowRadius="10"
android:textAppearance="@style/TextViewHeadline5"
android:textColor="@color/md_white_1000"
android:visibility="gone"
@ -34,7 +34,7 @@
android:layout_gravity="center_vertical"
android:gravity="center"
android:shadowColor="@color/md_black_1000"
android:shadowRadius="4"
android:shadowRadius="10"
android:textAppearance="@style/TextViewHeadline5"
android:textColor="@color/md_white_1000"
tools:text="@tools:sample/full_names[5]" />

View file

@ -127,8 +127,9 @@
android:name="code.name.monkey.retromusic.fragments.player.CoverLyricsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:elevation="20dp"
app:layout_constraintBottom_toTopOf="@+id/playbackControlsFragment"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/playerToolbar" />
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -58,7 +58,8 @@
app:layout_constraintBottom_toBottomOf="@+id/playerAlbumCoverFragment"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status_bar" />
android:layout_marginBottom="16dp"
tools:layout="@layout/fragment_cover_lyrics" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -32,33 +32,52 @@
app:contentInsetStart="0dp"
app:contentInsetStartWithNavigation="0dp"
app:navigationIcon="@drawable/ic_keyboard_backspace_black"
app:subtitleTextAppearance="@style/TextViewCaption"
app:title="@string/lyrics"
app:titleMargin="0dp"
app:titleMarginStart="0dp"
app:titleTextAppearance="@style/TextViewSubtitle1">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLyrics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tabIndicator="@drawable/tab_lyrics_indicator"
app:tabIndicatorAnimationMode="elastic"
app:tabIndicatorFullWidth="false"
app:tabIndicatorHeight="5dp" />
</com.google.android.material.appbar.MaterialToolbar>
app:titleTextAppearance="@style/ToolbarTextAppearanceNormal" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/lyricsPager"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
android:layout_marginTop="?attr/actionBarSize">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
<TextView
android:id="@+id/normalLyrics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/normal_lyrics_padding"
android:textIsSelectable="true"
android:textSize="@dimen/lyrics_text_size"
tools:text="@tools:sample/lorem[100]" />
</ScrollView>
<code.name.monkey.retromusic.lyrics.LrcView
android:id="@+id/lyricsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:lrcLabel="@string/no_lyrics_found"
app:lrcNormalTextSize="24sp"
app:lrcPadding="24dp"
app:lrcTextGravity="left"
app:lrcTextSize="28sp" />
<TextView
android:id="@+id/noLyricsFound"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_lyrics_found"
android:textSize="24sp"
android:visibility="gone" />
</FrameLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/edit_button"

View file

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/nomal_lyrics_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
<TextView
android:id="@+id/normalLyrics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/normal_lyrics_padding"
android:textIsSelectable="true"
android:textSize="@dimen/lyrics_text_size" />
</ScrollView>
<TextView
android:id="@+id/noLyricsFound"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_lyrics_found"
android:textSize="24sp"
android:visibility="gone" />
</FrameLayout>

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/synced_lyrics_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<code.name.monkey.retromusic.lyrics.LrcView
android:id="@+id/lyricsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:lrcLabel="@string/no_lyrics_found"
app:lrcNormalTextSize="24sp"
app:lrcPadding="24dp"
app:lrcTextGravity="left"
app:lrcTextSize="28sp" />
</FrameLayout>

View file

@ -42,7 +42,7 @@
</FrameLayout>
<code.name.monkey.retromusic.views.BottomNavigationBarTinted
<code.name.monkey.retromusic.views.TintedBottomNavigationView
android:id="@+id/navigationView"
style="@style/Widget.Material3.BottomNavigationView"
android:layout_width="match_parent"

View file

@ -125,5 +125,5 @@
<fragment
android:id="@+id/lyrics_fragment"
android:name="code.name.monkey.retromusic.fragments.other.LyricsFragment" />
android:name="code.name.monkey.retromusic.fragments.lyrics.LyricsFragment" />
</navigation>

View file

@ -94,6 +94,7 @@
<string name="black_theme_name">Just Black</string>
<string name="blacklist">Blacklist</string>
<string name="bluetooth_summary">The app needs nearby devices permission to check for bluetooth devices</string>
<string name="bluetooth_title">Nearby devices</string>
<string name="blur">Blur</string>
<string name="blur_card">Blur Card</string>
<string name="bug_report_failed">Unable to send report</string>
@ -561,5 +562,4 @@
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string>
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
<string name="bluetooth_title">Nearby devices</string>
</resources>

View file

@ -56,14 +56,6 @@
android:title="@string/pref_title_toggle_toggle_headset"
app:icon="@drawable/ic_play_arrow" />
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
android:defaultValue="false"
android:key="toggle_shuffle"
android:layout="@layout/list_item_view_switch"
android:summary="@string/pref_summary_toggle_shuffle"
android:title="@string/pref_title_toggle_toggle_shuffle"
app:icon="@drawable/ic_shuffle" />
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
android:defaultValue="false"
android:key="bluetooth_playback"

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="code.name.monkey.retromusic">
<uses-permission android:name="com.android.vending.BILLING" />
<application tools:ignore="MissingApplicationIcon">
<activity android:name=".activities.PurchaseActivity" />
<meta-data
android:name="com.android.vending.splits.required"
android:value="true" />
<!-- ChromeCast -->
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="code.name.monkey.retromusic.cast.CastOptionsProvider" />
</application>
</manifest>

View file

@ -1,17 +1,3 @@
/*
* Copyright (c) 2020 Hemanth Savarla.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*/
package code.name.monkey.retromusic.activities
import android.content.res.ColorStateList
@ -22,7 +8,7 @@ import android.view.MenuItem
import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
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.ActivityProVersionBinding
@ -58,7 +44,7 @@ class PurchaseActivity : AbsThemeActivity(), BillingProcessor.IBillingHandler {
restorePurchase()
}
binding.purchaseButton.setOnClickListener {
billingProcessor.purchase(this@PurchaseActivity, PRO_VERSION_PRODUCT_ID)
billingProcessor.purchase(this@PurchaseActivity, Constants.PRO_VERSION_PRODUCT_ID)
}
binding.bannerContainer.backgroundTintList =
ColorStateList.valueOf(accentColor())

View file

@ -3,6 +3,7 @@ package code.name.monkey.retromusic.activities.base
import code.name.monkey.retromusic.cast.RetroSessionManagerListener
import code.name.monkey.retromusic.cast.RetroWebServer
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.service.CastPlayer
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.common.ConnectionResult
@ -37,7 +38,7 @@ abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
override fun onSessionStarted(castSession: CastSession, p1: String) {
invalidateOptionsMenu()
mCastSession = castSession
MusicPlayerRemote.switchToRemotePlayback(castSession)
MusicPlayerRemote.switchToRemotePlayback(CastPlayer(castSession))
}
override fun onSessionEnded(castSession: CastSession, p1: Int) {
@ -53,7 +54,7 @@ abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
invalidateOptionsMenu()
mCastSession = castSession
webServer.start()
MusicPlayerRemote.switchToRemotePlayback(castSession)
MusicPlayerRemote.switchToRemotePlayback(CastPlayer(castSession))
}
override fun onSessionSuspended(castSession: CastSession, p1: Int) {

View file

@ -0,0 +1,37 @@
package code.name.monkey.retromusic.billing
import android.content.Context
import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.Constants
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.showToast
import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.PurchaseInfo
class BillingManager(context: Context) {
private val billingProcessor: BillingProcessor
init {
// automatically restores purchases
billingProcessor = BillingProcessor(
context, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
object : BillingProcessor.IBillingHandler {
override fun onProductPurchased(productId: String, details: PurchaseInfo?) {}
override fun onPurchaseHistoryRestored() {
context.showToast(R.string.restored_previous_purchase_please_restart)
}
override fun onBillingError(errorCode: Int, error: Throwable?) {}
override fun onBillingInitialized() {}
})
}
fun release() {
billingProcessor.release()
}
val isProVersion: Boolean
get() = billingProcessor.isPurchased(Constants.PRO_VERSION_PRODUCT_ID)
}

View file

@ -0,0 +1,25 @@
@file:Suppress("unused")
package code.name.monkey.retromusic.cast
import android.content.Context
import com.google.android.gms.cast.CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.SessionProvider
import com.google.android.gms.cast.framework.media.CastMediaOptions
class CastOptionsProvider : OptionsProvider {
override fun getCastOptions(context: Context): CastOptions {
val mediaOptions = CastMediaOptions.Builder().setNotificationOptions(null).build()
return CastOptions.Builder()
.setReceiverApplicationId(DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
.setCastMediaOptions(mediaOptions)
.build()
}
override fun getAdditionalSessionProviders(context: Context): MutableList<SessionProvider>? {
return null
}
}

View file

@ -0,0 +1,59 @@
package code.name.monkey.retromusic.extensions
import android.content.Context
import android.content.Intent
import android.view.Menu
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.PurchaseActivity
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.play.core.splitcompat.SplitCompat
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory
import com.google.android.play.core.splitinstall.SplitInstallRequest
import com.google.android.play.core.splitinstall.SplitInstallSessionState
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
import java.util.*
fun Context.setUpMediaRouteButton(menu: Menu) {
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.action_cast)
}
fun FragmentActivity.installLanguageAndRecreate(code: String) {
var mySessionId = 0
val manager = SplitInstallManagerFactory.create(this)
val listener = object: SplitInstallStateUpdatedListener{
override fun onStateUpdate(state: SplitInstallSessionState) {
if (state.sessionId() == mySessionId) {
recreate()
manager.unregisterListener(this)
}
}
}
manager.registerListener(listener)
if (code != "auto") {
// Try to download language resources
val request =
SplitInstallRequest.newBuilder().addLanguage(Locale.forLanguageTag(code))
.build()
manager.startInstall(request)
// Recreate the activity on download complete
.addOnSuccessListener {
mySessionId = it
}
.addOnFailureListener {
showToast("Language download failed.")
}
} else {
recreate()
}
}
fun Context.goToProVersion() {
startActivity(Intent(this, PurchaseActivity::class.java))
}
fun Context.installSplitCompat() {
SplitCompat.install(this)
}

View file

@ -28,7 +28,6 @@ object AppRater {
private const val DAYS_UNTIL_PROMPT = 3//Min number of days
private const val LAUNCHES_UNTIL_PROMPT = 5//Min number of launches
@JvmStatic
fun appLaunched(context: Activity) {
val prefs = context.getSharedPreferences(APP_RATING, 0)
if (prefs.getBoolean(DO_NOT_SHOW_AGAIN, false)) {

View file

@ -1,52 +0,0 @@
package code.name.monkey.appthemehelper.util
import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import code.name.monkey.appthemehelper.ThemeStore
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationView
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object NavigationViewUtil {
fun setItemIconColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val iconSl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_checked),
intArrayOf(android.R.attr.state_checked)
), intArrayOf(normalColor, selectedColor)
)
navigationView.itemIconTintList = iconSl
val drawable = navigationView.itemBackground
navigationView.itemBackground = TintHelper.createTintedDrawable(
drawable,
ColorUtil.withAlpha(ThemeStore.accentColor(navigationView.context), 0.2f)
)
}
fun setItemTextColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val textSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor)
)
navigationView.itemTextColor = textSl
}
fun setItemIconColors(bottomNavigationView: BottomNavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val iconSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor)
)
bottomNavigationView.itemIconTintList = iconSl
}
fun setItemTextColors(bottomNavigationView: BottomNavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val textSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor)
)
bottomNavigationView.itemTextColor = textSl
}
}

View file

@ -3,10 +3,12 @@
buildscript {
ext {
kotlin_version = '1.7.0'
navigation_version = '2.5.0-rc01'
lifecycle_version='2.5.0-rc02'
navigation_version = '2.5.0-rc02'
mdc_version = '1.7.0-alpha02'
preference_version = '1.2.0'
appcompat_version = '1.4.2'
core_version='1.8.0'
}
repositories {

View file

@ -0,0 +1 @@
Added lyrics downloading

View file

@ -0,0 +1,20 @@
Retro Music Player 🎵
<b>📦 Included Features</b>
<ul>
<li>Base 3 themes (Clearly White, Kinda Dark and Just Black)</li>
<li>Material You support on Android 12+</li>
<li>Gapless playback</li>
<li>Crossfade playback</li>
<li>Choose from 10+ now playing themes</li>
<li>Android auto support</li>
<li>Wallpaper accent picker on Android 8.1+</li>
<li>Home screen widgets</li>
<li>Lock screen playback controls</li>
<li>Sleep timer</li>
<li>Easy drag to sort playlist & play queue</li>
<li>Tag editor</li>
<li>Create, edit and import playlists</li>
<li>Browse and play your music by songs, albums, artists, playlists and genre</li>
<li>Smart Auto Playlists - Recently played, most played and history</li>
</ul>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

Before

Width:  |  Height:  |  Size: 578 KiB

After

Width:  |  Height:  |  Size: 578 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 692 KiB

After

Width:  |  Height:  |  Size: 692 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 687 KiB

After

Width:  |  Height:  |  Size: 687 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 688 KiB

After

Width:  |  Height:  |  Size: 688 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 355 KiB

After

Width:  |  Height:  |  Size: 355 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 928 KiB

After

Width:  |  Height:  |  Size: 928 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 985 KiB

After

Width:  |  Height:  |  Size: 985 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 328 KiB

After

Width:  |  Height:  |  Size: 328 KiB

Before After
Before After

View file

@ -0,0 +1 @@
Material You Design Music player for Android