This commit is contained in:
Hemanth S 2021-12-20 17:14:45 +05:30
commit 80739f412a
105 changed files with 2235 additions and 625 deletions

View file

@ -33,7 +33,7 @@ android {
versionNameSuffix "_" + getDate() versionNameSuffix "_" + getDate()
shrinkResources true shrinkResources true
minifyEnabled true minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release signingConfig signingConfigs.release
} }
debug { debug {
@ -95,7 +95,7 @@ dependencies {
implementation 'androidx.annotation:annotation:1.3.0' implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01' implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01'
implementation 'androidx.preference:preference-ktx:1.2.0-beta01' implementation "androidx.preference:preference-ktx:$preference_version"
implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.palette:palette-ktx:1.0.0' implementation 'androidx.palette:palette-ktx:1.0.0'
@ -105,12 +105,11 @@ dependencies {
//WebServer by NanoHttpd //WebServer by NanoHttpd
implementation "org.nanohttpd:nanohttpd:2.3.1" implementation "org.nanohttpd:nanohttpd:2.3.1"
def nav_version = '2.4.0-beta02' implementation "androidx.navigation:navigation-runtime-ktx:$navigation_version"
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version" implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
def room_version = '2.4.0-rc01' def room_version = '2.4.0'
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version" implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"
@ -121,7 +120,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation 'com.google.android.play:core-ktx:1.8.1' implementation 'com.google.android.play:core-ktx:1.8.1'
implementation 'com.google.android.material:material:1.5.0-beta01' implementation "com.google.android.material:material:$mdc_version"
def retrofit_version = '2.9.0' def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
@ -138,7 +137,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
def kotlin_coroutines_version = '1.6.0-RC' def kotlin_coroutines_version = '1.6.0-RC3'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
@ -153,12 +152,13 @@ dependencies {
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5' implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3' implementation 'com.github.Adonai:jaudiotagger:2.3.15'
implementation 'com.anjlab.android.iab.v3:library:2.0.3' implementation 'com.anjlab.android.iab.v3:library:2.0.3'
implementation 'com.r0adkll:slidableactivity:2.1.0' implementation 'com.r0adkll:slidableactivity:2.1.0'
implementation 'com.heinrichreimersoftware:material-intro:2.0.0' implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
implementation 'com.github.dhaval2404:imagepicker:2.1' implementation 'com.github.dhaval2404:imagepicker:2.1'
implementation 'me.zhanghai.android.fastscroll:library:1.1.7' implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
implementation 'cat.ereza:customactivityoncrash:2.3.0' implementation 'cat.ereza:customactivityoncrash:2.3.0'
implementation 'me.tankery.lib:circularSeekBar:1.3.2'
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6' debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'
} }

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<bool name="md3_available">true</bool> <bool name="md3_available">true</bool>
<bool name="allowBackup">false</bool>
</resources> </resources>

View file

@ -22,7 +22,7 @@
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="@bool/allowBackup"
android:configChanges="locale|layoutDirection" android:configChanges="locale|layoutDirection"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
@ -123,15 +123,38 @@
<activity android:name=".activities.LockScreenActivity" /> <activity android:name=".activities.LockScreenActivity" />
<activity <activity
android:name=".fragments.backup.RestoreActivity" android:name=".fragments.backup.RestoreActivity"
android:exported="true"> android:excludeFromRecents="false"
android:exported="true"
android:label="@string/restore"
android:theme="@style/Theme.RetroMusic.Dialog">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="application/octet-stream" />
<data android:mimeType="application/x-zip-compressed" />
<data android:mimeType="application/zip" />
<data android:scheme="file" /> <data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" /> <data android:mimeType="*/*" />
<!--
Work around Android's ugly primitive PatternMatcher
implementation that can't cope with finding a . early in
the path unless it's explicitly matched.
-->
<data android:host="*" />
<data android:pathPattern=".*\\.rmbak" /> <data android:pathPattern=".*\\.rmbak" />
<data android:pathPattern=".*\\..*\\.rmbak" />
<data android:pathPattern=".*\\..*\\..*\\.rmbak" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.rmbak" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rmbak" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
</intent-filter> </intent-filter>
</activity> </activity>
@ -273,10 +296,9 @@
<service <service
android:name=".service.MusicService" android:name=".service.MusicService"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="false"
android:foregroundServiceType="mediaPlayback" android:foregroundServiceType="mediaPlayback"
android:label="@string/app_name" android:label="@string/app_name">
tools:ignore="ExportedService">
<intent-filter> <intent-filter>
<action android:name="android.media.browse.MediaBrowserService" /> <action android:name="android.media.browse.MediaBrowserService" />
</intent-filter> </intent-filter>

View file

@ -24,40 +24,48 @@
padding-top: 8px; padding-top: 8px;
} }
</style> </style>
</head> </head>
<body> <body>
<p><b><a href="https://github.com/kabouzeid/Phonograph" title="Phonograph"> Phonograph</a></b> by <p><b><a href="https://github.com/kabouzeid/Phonograph" title="Phonograph"> Phonograph</a></b> by
Karim Abou Zeid</p> Karim Abou Zeid</p>
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
by Aidan Michael Follestad</p>
<p><b><a href="http://developer.android.com/tools/support-library/index.html" <p><b><a href="http://developer.android.com/tools/support-library/index.html"
title="AOSP Support Libraries"> AOSP Support Libraries</a></b>by AOSP contributors</p> title="AOSP Support Libraries">AOSP Support Libraries</a></b> by AOSP contributors</p>
<p><b><a href="https://github.com/bumptech/glide" title="Glide"> Glide</a></b> by Sam Judd</p> <p><b><a href="https://github.com/bumptech/glide" title="Glide"> Glide</a></b> by Sam Judd</p>
<p><b><a href="https://github.com/square/retrofit" title="Retrofit"> Retrofit</a></b> by Square team <p><b><a href="https://github.com/square/retrofit" title="Retrofit"> Retrofit</a></b> by Square team
</p> </p>
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
<p><b><a href="https://github.com/InsertKoinIO/koin"
title="Koin">Koin</a></b> by Arnaud Giuliani</p>
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
by Aidan Michael Follestad</p>
<p><b><a href="https://github.com/afollestad/material-cab" title="Material Contextual Action Bar"> <p><b><a href="https://github.com/afollestad/material-cab" title="Material Contextual Action Bar">
Material Contextual Action Bar</a></b> by Aidan Michael Follestad</p> Material Contextual Action Bar</a></b> by Aidan Michael Follestad</p>
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
<p><b><a href="https://github.com/hdodenhof/CircleImageView" title="CircleImageView">
CircleImageView</a></b> by Henning Dodenhof</p>
<p><b><a href="https://github.com/DreaminginCodeZH/MaterialProgressBar" title="MaterialProgressBar">
MaterialProgressBar</a></b> by Zhang Hai</p>
<p><b><a href="https://github.com/anjlab/android-inapp-billing-v3" <p><b><a href="https://github.com/anjlab/android-inapp-billing-v3"
title="Android In-App Billing v3 Library"> Android In-App Billing v3 Library</a></b> by title="Android In-App Billing v3 Library"> Android In-App Billing v3 Library</a></b> by
Henning Dodenhof</p> Henning Dodenhof</p>
<p><b><a href="https://github.com/h6ah4i/android-advancedrecyclerview" <p><b><a href="https://github.com/h6ah4i/android-advancedrecyclerview"
title="Advanced RecyclerView"> Advanced RecyclerView</a></b> by Haruki Hasegawa</p> title="Advanced RecyclerView"> Advanced RecyclerView</a></b> by Haruki Hasegawa</p>
<p><b><a href="https://github.com/ksoichiro/Android-ObservableScrollView" <p><b><a href="https://github.com/Ereza/CustomActivityOnCrash"
title="Android-ObservableScrollView"> Android-ObservableScrollView</a></b> by Soichiro title="Custom Activity on Crash">Custom Activity on Crash</a></b> by Eduard Ereza Martínez
Kashima</p> </p>
<p><b><a href="https://github.com/NanoHttpd/nanohttpd"
title="NanoHttpd">NanoHttpd</a></b> by NanoHttpd Team</p>
<p><b><a href="https://github.com/tankery/CircularSeekBar"
title="Circular Seekbar">Circular Seekbar</a></b> by Tankery</p>
<p><b><a href="https://github.com/Kaned1as/jaudiotagger"
title="jAudioTagger">jAudioTagger</a></b> by Kanedias</p>
<p><b><a href="https://github.com/zhanghai/AndroidFastScroll"
title="Android Fast Scroll">Android Fast Scroll</a></b> by Zhang Hai</p>
<p><b><a href="https://github.com/Dhaval2404/ImagePicker"
title="Image Picker">Image Picker</a></b> by Dhaval Patel</p>
<p><b><a href="https://github.com/heinrichreimer/material-intro"
title="Material Intro">Material Intro</a></b> by Jan Heinrich Reimer</p>
<p><b><a href="https://github.com/r0adkll/Slidr"
title="Slidr">Slidr</a></b> by Drew Heavner</p>
<p><b><a href="https://materialdesignicons.com" title="Icons"> Icons</a></b> by Austin Andrews</p> <p><b><a href="https://materialdesignicons.com" title="Icons"> Icons</a></b> by Austin Andrews</p>
<p><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b> <p><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b>
</p> </p>
</body> </body>
</html> </html>

View file

@ -21,7 +21,6 @@ import android.graphics.PorterDuff
import android.os.Bundle import android.os.Bundle
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.SeekBar import android.widget.SeekBar
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
@ -29,8 +28,6 @@ import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.databinding.ActivityDriveModeBinding import code.name.monkey.retromusic.databinding.ActivityDriveModeBinding
import code.name.monkey.retromusic.db.toSongEntity import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.glide.BlurTransformation import code.name.monkey.retromusic.glide.BlurTransformation
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
@ -50,7 +47,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
/** /**

View file

@ -16,8 +16,6 @@ package code.name.monkey.retromusic.activities
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.webkit.WebView
import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
@ -25,29 +23,30 @@ import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsThemeActivity import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.databinding.ActivityLicenseBinding
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import java.io.BufferedReader import java.io.BufferedReader
import java.io.InputStreamReader import java.io.InputStreamReader
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
/** Created by hemanths on 2019-09-27. */ /** Created by hemanths on 2019-09-27. */
class LicenseActivity : AbsThemeActivity() { class LicenseActivity : AbsThemeActivity() {
private lateinit var binding: ActivityLicenseBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_license) binding = ActivityLicenseBinding.inflate(layoutInflater)
val toolbar = findViewById<Toolbar>(R.id.toolbar) setContentView(binding.root)
setSupportActionBar(toolbar) setSupportActionBar(binding.toolbar)
ToolbarContentTintHelper.colorBackButton(toolbar) ToolbarContentTintHelper.colorBackButton(binding.toolbar)
toolbar.setBackgroundColor(resolveColor(this, R.attr.colorSurface))
val webView = findViewById<WebView>(R.id.license)
try { try {
val buf = StringBuilder() val buf = StringBuilder()
val json = assets.open("oldindex.html") val json = assets.open("oldindex.html")
val br = BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)) BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)).use { br ->
var str: String? var str: String?
while (br.readLine().also { str = it } != null) { while (br.readLine().also { str = it } != null) {
buf.append(str) buf.append(str)
}
} }
br.close()
// Inject color values for WebView body background and links // Inject color values for WebView body background and links
val isDark = isWindowBackgroundDark(this) val isDark = isWindowBackgroundDark(this)
@ -72,12 +71,13 @@ class LicenseActivity : AbsThemeActivity() {
lightenColor(accentColor(this)) lightenColor(accentColor(this))
) )
) )
webView.loadData(changeLog, "text/html", "UTF-8") binding.license.loadData(changeLog, "text/html", "UTF-8")
} catch (e: Throwable) { } catch (e: Throwable) {
webView.loadData( binding.license.loadData(
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8" "<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8"
) )
} }
binding.license.drawAboveSystemBars()
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

View file

@ -22,9 +22,9 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.View
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.view.isVisible
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
@ -84,12 +84,12 @@ class PermissionActivity : AbsMusicServiceActivity() {
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
override fun onResume() { override fun onResume() {
if (hasStoragePermission()) { if (hasStoragePermission()) {
binding.storagePermission.checkImage.visibility = View.VISIBLE binding.storagePermission.checkImage.isVisible = true
binding.storagePermission.checkImage.imageTintList = binding.storagePermission.checkImage.imageTintList =
ColorStateList.valueOf(ThemeStore.accentColor(this)) ColorStateList.valueOf(ThemeStore.accentColor(this))
} }
if (hasAudioPermission()) { if (hasAudioPermission()) {
binding.audioPermission.checkImage.visibility = View.VISIBLE binding.audioPermission.checkImage.isVisible = true
binding.audioPermission.checkImage.imageTintList = binding.audioPermission.checkImage.imageTintList =
ColorStateList.valueOf(ThemeStore.accentColor(this)) ColorStateList.valueOf(ThemeStore.accentColor(this))
} }

View file

@ -34,12 +34,16 @@ class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListen
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
val mSavedInstanceState = extra<Bundle>(TAG).value ?: savedInstanceState val mSavedInstanceState = extra<Bundle>(TAG).value ?: savedInstanceState
super.onCreate(mSavedInstanceState) super.onCreate(mSavedInstanceState)
setLightStatusBarAuto(surfaceColor())
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setupToolbar() setupToolbar()
} }
override fun onResume() {
super.onResume()
setNavigationBarColorPreOreo(surfaceColor())
}
private fun setupToolbar() { private fun setupToolbar() {
applyToolbar(binding.toolbar) applyToolbar(binding.toolbar)
val navController: NavController = findNavController(R.id.contentFrame) val navController: NavController = findNavController(R.id.contentFrame)
@ -81,7 +85,6 @@ class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListen
ThemeStore.editTheme(this).accentColor(color).commit() ThemeStore.editTheme(this).accentColor(color).commit()
if (VersionUtils.hasNougatMR()) if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).updateDynamicShortcuts() DynamicShortcutManager(this).updateDynamicShortcuts()
restart() restart()
} }

View file

@ -17,6 +17,7 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsThemeActivity import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.databinding.ActivityWhatsNewBinding import code.name.monkey.retromusic.databinding.ActivityWhatsNewBinding
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.extensions.setLightStatusBarAuto import code.name.monkey.retromusic.extensions.setLightStatusBarAuto
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion
@ -38,12 +39,12 @@ class WhatsNewActivity : AbsThemeActivity() {
try { try {
val buf = StringBuilder() val buf = StringBuilder()
val json = assets.open("retro-changelog.html") val json = assets.open("retro-changelog.html")
val br = BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)) BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)).use { br ->
var str: String? var str: String?
while (br.readLine().also { str = it } != null) { while (br.readLine().also { str = it } != null) {
buf.append(str) buf.append(str)
}
} }
br.close()
// Inject color values for WebView body background and links // Inject color values for WebView body background and links
val isDark = isWindowBackgroundDark(this) val isDark = isWindowBackgroundDark(this)
@ -100,6 +101,7 @@ class WhatsNewActivity : AbsThemeActivity() {
binding.tgFab.extend() binding.tgFab.extend()
} }
} }
binding.webView.drawAboveSystemBars()
} }
companion object { companion object {

View file

@ -14,12 +14,15 @@
*/ */
package code.name.monkey.retromusic.activities.base package code.name.monkey.retromusic.activities.base
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.view.animation.PathInterpolator
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
@ -28,7 +31,6 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commit import androidx.fragment.app.commit
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior import code.name.monkey.retromusic.RetroBottomSheetBehavior
@ -61,9 +63,11 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.CategoryInfo import code.name.monkey.retromusic.model.CategoryInfo
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.ViewUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior.* import com.google.android.material.bottomsheet.BottomSheetBehavior.*
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
companion object { companion object {
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
@ -78,14 +82,27 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
private var nowPlayingScreen: NowPlayingScreen? = null private var nowPlayingScreen: NowPlayingScreen? = null
private var taskColor: Int = 0 private var taskColor: Int = 0
private var paletteColor: Int = Color.WHITE private var paletteColor: Int = Color.WHITE
private var navigationBarColor = 0
protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding
private val panelState: Int private val panelState: Int
get() = bottomSheetBehavior.state get() = bottomSheetBehavior.state
private lateinit var binding: SlidingMusicPanelLayoutBinding private lateinit var binding: SlidingMusicPanelLayoutBinding
private var navigationBarColorAnimator: ValueAnimator? = null
private val argbEvaluator: ArgbEvaluator = ArgbEvaluator()
private val bottomSheetCallbackList = object : BottomSheetCallback() { private val bottomSheetCallbackList = object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) { override fun onSlide(bottomSheet: View, slideOffset: Float) {
setMiniPlayerAlphaProgress(slideOffset) setMiniPlayerAlphaProgress(slideOffset)
navigationBarColorAnimator?.cancel()
setNavigationBarColorPreOreo(
argbEvaluator.evaluate(
slideOffset,
surfaceColor(),
navigationBarColor
) as Int
)
} }
override fun onStateChanged(bottomSheet: View, newState: Int) { override fun onStateChanged(bottomSheet: View, newState: Int) {
@ -131,6 +148,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
updateColor() updateColor()
binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor()) binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
bottomNavigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor()) bottomNavigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
navigationBarColor = surfaceColor()
} }
private fun setupBottomSheet() { private fun setupBottomSheet() {
@ -177,9 +195,25 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
} }
private fun animateNavigationBarColor(color: Int) {
navigationBarColorAnimator?.cancel()
navigationBarColorAnimator = ValueAnimator
.ofArgb(window.navigationBarColor, color).apply {
duration = ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong()
interpolator = PathInterpolator(0.4f, 0f, 1f, 1f)
addUpdateListener { animation: ValueAnimator ->
setNavigationBarColorPreOreo(
animation.animatedValue as Int
)
}
start()
}
}
open fun onPanelCollapsed() { open fun onPanelCollapsed() {
setMiniPlayerAlphaProgress(0F) setMiniPlayerAlphaProgress(0F)
// restore values // restore values
animateNavigationBarColor(surfaceColor())
setLightStatusBarAuto(surfaceColor()) setLightStatusBarAuto(surfaceColor())
setLightNavigationAuto() setLightNavigationAuto()
setTaskDescriptionColor(taskColor) setTaskDescriptionColor(taskColor)
@ -252,18 +286,25 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
private fun onPaletteColorChanged() { private fun onPaletteColorChanged() {
if (panelState == STATE_EXPANDED) { if (panelState == STATE_EXPANDED) {
navigationBarColor = surfaceColor()
setTaskDescColor(paletteColor) setTaskDescColor(paletteColor)
val isColorLight = ColorUtil.isColorLight(paletteColor) val isColorLight = ColorUtil.isColorLight(paletteColor)
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat)) { if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat)) {
setLightNavigationBar(true) setLightNavigationBar(true)
setLightStatusBar(isColorLight) setLightStatusBar(isColorLight)
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) { } else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
animateNavigationBarColor(Color.BLACK)
navigationBarColor = Color.BLACK
setLightStatusBar(false) setLightStatusBar(false)
setLightNavigationBar(true) setLightNavigationBar(true)
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) { } else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
animateNavigationBarColor(paletteColor)
navigationBarColor = paletteColor
setLightNavigationBar(isColorLight) setLightNavigationBar(isColorLight)
setLightStatusBar(isColorLight) setLightStatusBar(isColorLight)
} else if (nowPlayingScreen == Full) { } else if (nowPlayingScreen == Full) {
animateNavigationBarColor(paletteColor)
navigationBarColor = paletteColor
setLightNavigationBar(isColorLight) setLightNavigationBar(isColorLight)
setLightStatusBar(false) setLightStatusBar(false)
} else if (nowPlayingScreen == Classic) { } else if (nowPlayingScreen == Classic) {
@ -273,10 +314,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
} else { } else {
setLightStatusBar( setLightStatusBar(
ColorUtil.isColorLight( ColorUtil.isColorLight(
ATHUtil.resolveColor( surfaceColor()
this,
android.R.attr.windowBackground
)
) )
) )
setLightNavigationBar(true) setLightNavigationBar(true)

View file

@ -34,7 +34,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
@ -60,14 +59,13 @@ import java.io.File
import java.util.* import java.util.*
abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() { abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
abstract val editorImage: ImageView? abstract val editorImage: ImageView
val repository by inject<Repository>() val repository by inject<Repository>()
lateinit var saveFab: MaterialButton lateinit var saveFab: MaterialButton
protected var id: Long = 0 protected var id: Long = 0
private set private set
private var paletteColorPrimary: Int = 0 private var paletteColorPrimary: Int = 0
private var isInNoImageMode: Boolean = false
private var songPaths: List<String>? = null private var songPaths: List<String>? = null
private var savedSongPaths: List<String>? = null private var savedSongPaths: List<String>? = null
private val currentSongPath: String? = null private val currentSongPath: String? = null
@ -176,6 +174,15 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
} }
} }
protected val discNumber: String?
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO)
} catch (ignored: Exception) {
null
}
}
protected val lyrics: String? protected val lyrics: String?
get() { get() {
return try { return try {
@ -239,7 +246,7 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
getString(R.string.web_search), getString(R.string.web_search),
getString(R.string.remove_cover) getString(R.string.remove_cover)
) )
editorImage?.setOnClickListener { show } editorImage.setOnClickListener { show }
} }
private fun startImagePicker() { private fun startImagePicker() {
@ -306,17 +313,6 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
protected fun setNoImageMode() {
isInNoImageMode = true
setColors(
intent.getIntExtra(
EXTRA_PALETTE,
ATHUtil.resolveColor(this, R.attr.colorPrimary)
)
)
}
protected fun dataChanged() { protected fun dataChanged() {
showFab() showFab()
} }
@ -335,9 +331,9 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) { protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
if (bitmap == null) { if (bitmap == null) {
editorImage?.setImageResource(drawable.default_audio_art) editorImage.setImageResource(drawable.default_audio_art)
} else { } else {
editorImage?.setImageBitmap(bitmap) editorImage.setImageBitmap(bitmap)
} }
setColors(bgColor) setColors(bgColor)
} }

View file

@ -29,9 +29,11 @@ import android.view.LayoutInflater
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
import code.name.monkey.retromusic.extensions.appHandleColor import code.name.monkey.retromusic.extensions.appHandleColor
import code.name.monkey.retromusic.extensions.isColorLight
import code.name.monkey.retromusic.extensions.setTint import code.name.monkey.retromusic.extensions.setTint
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
@ -63,41 +65,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
window.enterTransition = slide window.enterTransition = slide
} }
override fun loadImageFromFile(selectedFile: Uri?) {
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
getColor(resource.palette, Color.TRANSPARENT)
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
setImageBitmap(
albumArtBitmap,
getColor(
resource.palette,
ATHUtil.resolveColor(
this@AlbumTagEditorActivity,
R.attr.defaultFooterColor
)
)
)
deleteAlbumArt = false
dataChanged()
setResult(Activity.RESULT_OK)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
Toast.makeText(this@AlbumTagEditorActivity, "Load Failed", Toast.LENGTH_LONG)
.show()
}
override fun setResource(resource: BitmapPaletteWrapper?) {}
})
}
private var albumArtBitmap: Bitmap? = null private var albumArtBitmap: Bitmap? = null
private var deleteAlbumArt: Boolean = false private var deleteAlbumArt: Boolean = false
@ -171,6 +138,41 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
dataChanged() dataChanged()
} }
override fun loadImageFromFile(selectedFile: Uri?) {
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
getColor(resource.palette, Color.TRANSPARENT)
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
setImageBitmap(
albumArtBitmap,
getColor(
resource.palette,
ATHUtil.resolveColor(
this@AlbumTagEditorActivity,
R.attr.defaultFooterColor
)
)
)
deleteAlbumArt = false
dataChanged()
setResult(Activity.RESULT_OK)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
Toast.makeText(this@AlbumTagEditorActivity, "Load Failed", Toast.LENGTH_LONG)
.show()
}
override fun setResource(resource: BitmapPaletteWrapper?) {}
})
}
override fun save() { override fun save() {
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java) val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString() fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
@ -213,6 +215,16 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
override fun setColors(color: Int) { override fun setColors(color: Int) {
super.setColors(color) super.setColors(color)
saveFab.backgroundTintList = ColorStateList.valueOf(color) saveFab.backgroundTintList = ColorStateList.valueOf(color)
saveFab.backgroundTintList = ColorStateList.valueOf(color)
ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor(
this,
color.isColorLight
)
).also {
saveFab.iconTint = it
saveFab.setTextColor(it)
}
} }

View file

@ -15,17 +15,36 @@
package code.name.monkey.retromusic.activities.tageditor package code.name.monkey.retromusic.activities.tageditor
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
import code.name.monkey.retromusic.extensions.appHandleColor import code.name.monkey.retromusic.extensions.appHandleColor
import code.name.monkey.retromusic.extensions.isColorLight
import code.name.monkey.retromusic.extensions.setTint import code.name.monkey.retromusic.extensions.setTint
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.model.ArtworkInfo
import code.name.monkey.retromusic.repository.SongRepository import code.name.monkey.retromusic.repository.SongRepository
import code.name.monkey.retromusic.util.ImageUtil
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.target.ImageViewTarget
import com.bumptech.glide.request.transition.Transition
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -39,12 +58,14 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
private val songRepository by inject<SongRepository>() private val songRepository by inject<SongRepository>()
private var albumArtBitmap: Bitmap? = null
private var deleteAlbumArt: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setUpViews() setUpViews()
setNoImageMode()
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
binding.appBarLayout.statusBarForeground = binding.appBarLayout?.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(this) MaterialShapeDrawable.createWithElevationOverlay(this)
} }
@ -59,6 +80,7 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
binding.yearContainer.setTint(false) binding.yearContainer.setTint(false)
binding.genreContainer.setTint(false) binding.genreContainer.setTint(false)
binding.trackNumberContainer.setTint(false) binding.trackNumberContainer.setTint(false)
binding.discNumberContainer.setTint(false)
binding.lyricsContainer.setTint(false) binding.lyricsContainer.setTint(false)
binding.songText.appHandleColor().addTextChangedListener(this) binding.songText.appHandleColor().addTextChangedListener(this)
@ -68,13 +90,9 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
binding.genreText.appHandleColor().addTextChangedListener(this) binding.genreText.appHandleColor().addTextChangedListener(this)
binding.yearText.appHandleColor().addTextChangedListener(this) binding.yearText.appHandleColor().addTextChangedListener(this)
binding.trackNumberText.appHandleColor().addTextChangedListener(this) binding.trackNumberText.appHandleColor().addTextChangedListener(this)
binding.discNumberText.appHandleColor().addTextChangedListener(this)
binding.lyricsText.appHandleColor().addTextChangedListener(this) binding.lyricsText.appHandleColor().addTextChangedListener(this)
binding.songComposerText.appHandleColor().addTextChangedListener(this) binding.songComposerText.appHandleColor().addTextChangedListener(this)
binding.lyricsText.setOnTouchListener { view, _ ->
view.parent.requestDisallowInterceptTouchEvent(true)
return@setOnTouchListener false
}
} }
private fun fillViewsWithFileTags() { private fun fillViewsWithFileTags() {
@ -85,16 +103,50 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
binding.genreText.setText(genreName) binding.genreText.setText(genreName)
binding.yearText.setText(songYear) binding.yearText.setText(songYear)
binding.trackNumberText.setText(trackNumber) binding.trackNumberText.setText(trackNumber)
binding.discNumberText.setText(discNumber)
binding.lyricsText.setText(lyrics) binding.lyricsText.setText(lyrics)
binding.songComposerText.setText(composer) binding.songComposerText.setText(composer)
println(songTitle + songYear) println(songTitle + songYear)
} }
override fun loadCurrentImage() {} override fun loadCurrentImage() {
val bitmap = albumArt
setImageBitmap(
bitmap,
RetroColorUtil.getColor(
RetroColorUtil.generatePalette(bitmap),
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
)
)
deleteAlbumArt = false
}
override fun searchImageOnWeb() {} override fun searchImageOnWeb() {
searchWebFor(binding.songText.text.toString(), binding.artistText.text.toString())
}
override fun deleteImage() {} override fun deleteImage() {
setImageBitmap(
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
)
deleteAlbumArt = true
dataChanged()
}
override fun setColors(color: Int) {
super.setColors(color)
saveFab.backgroundTintList = ColorStateList.valueOf(color)
ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor(
this,
color.isColorLight
)
).also {
saveFab.iconTint = it
saveFab.setTextColor(it)
}
}
override fun save() { override fun save() {
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java) val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
@ -104,10 +156,16 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
fieldKeyValueMap[FieldKey.GENRE] = binding.genreText.text.toString() fieldKeyValueMap[FieldKey.GENRE] = binding.genreText.text.toString()
fieldKeyValueMap[FieldKey.YEAR] = binding.yearText.text.toString() fieldKeyValueMap[FieldKey.YEAR] = binding.yearText.text.toString()
fieldKeyValueMap[FieldKey.TRACK] = binding.trackNumberText.text.toString() fieldKeyValueMap[FieldKey.TRACK] = binding.trackNumberText.text.toString()
fieldKeyValueMap[FieldKey.DISC_NO] = binding.discNumberText.text.toString()
fieldKeyValueMap[FieldKey.LYRICS] = binding.lyricsText.text.toString() fieldKeyValueMap[FieldKey.LYRICS] = binding.lyricsText.text.toString()
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString() fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
fieldKeyValueMap[FieldKey.COMPOSER] = binding.songComposerText.text.toString() fieldKeyValueMap[FieldKey.COMPOSER] = binding.songComposerText.text.toString()
writeValuesToFiles(fieldKeyValueMap, null) writeValuesToFiles(fieldKeyValueMap, when {
deleteAlbumArt -> ArtworkInfo(id, null)
albumArtBitmap == null -> null
else -> ArtworkInfo(id, albumArtBitmap!!)
}
)
} }
override fun getSongPaths(): List<String> = listOf(songRepository.song(id).data) override fun getSongPaths(): List<String> = listOf(songRepository.song(id).data)
@ -115,6 +173,38 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
override fun getSongUris(): List<Uri> = listOf(MusicUtil.getSongFileUri(id)) override fun getSongUris(): List<Uri> = listOf(MusicUtil.getSongFileUri(id))
override fun loadImageFromFile(selectedFile: Uri?) { override fun loadImageFromFile(selectedFile: Uri?) {
GlideApp.with(this@SongTagEditorActivity).asBitmapPalette().load(selectedFile)
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
override fun onResourceReady(
resource: BitmapPaletteWrapper,
transition: Transition<in BitmapPaletteWrapper>?
) {
RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
setImageBitmap(
albumArtBitmap,
RetroColorUtil.getColor(
resource.palette,
ATHUtil.resolveColor(
this@SongTagEditorActivity,
R.attr.defaultFooterColor
)
)
)
deleteAlbumArt = false
dataChanged()
setResult(Activity.RESULT_OK)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
Toast.makeText(this@SongTagEditorActivity, "Load Failed", Toast.LENGTH_LONG)
.show()
}
override fun setResource(resource: BitmapPaletteWrapper?) {}
})
} }
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
@ -131,6 +221,6 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
val TAG: String = SongTagEditorActivity::class.java.simpleName val TAG: String = SongTagEditorActivity::class.java.simpleName
} }
override val editorImage: ImageView? override val editorImage: ImageView
get() = null get() = binding.editorImage
} }

View file

@ -21,8 +21,8 @@ import org.jaudiotagger.audio.exceptions.CannotWriteException
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
import org.jaudiotagger.tag.TagException import org.jaudiotagger.tag.TagException
import org.jaudiotagger.tag.images.AndroidArtwork
import org.jaudiotagger.tag.images.Artwork import org.jaudiotagger.tag.images.Artwork
import org.jaudiotagger.tag.images.ArtworkFactory
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
@ -58,11 +58,11 @@ class TagWriter {
try { try {
albumArtFile = createAlbumArtFile(context).canonicalFile albumArtFile = createAlbumArtFile(context).canonicalFile
info.artworkInfo.artwork.compress( info.artworkInfo.artwork.compress(
Bitmap.CompressFormat.PNG, Bitmap.CompressFormat.JPEG,
0, 100,
FileOutputStream(albumArtFile) FileOutputStream(albumArtFile)
) )
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile) artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} }
@ -131,11 +131,11 @@ class TagWriter {
try { try {
albumArtFile = createAlbumArtFile(context).canonicalFile albumArtFile = createAlbumArtFile(context).canonicalFile
info.artworkInfo.artwork.compress( info.artworkInfo.artwork.compress(
Bitmap.CompressFormat.PNG, Bitmap.CompressFormat.JPEG,
0, 100,
FileOutputStream(albumArtFile) FileOutputStream(albumArtFile)
) )
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile) artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} }

View file

@ -33,9 +33,10 @@ import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.menu.SongMenuHelper import code.name.monkey.retromusic.helper.menu.SongMenuHelper
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.repository.PlaylistSongsLoader import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import java.util.* import java.util.*

View file

@ -4,9 +4,9 @@ import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R

View file

@ -30,7 +30,11 @@ class ImportPlaylistDialog : DialogFragment() {
return materialDialog(R.string.import_playlist) return materialDialog(R.string.import_playlist)
.setMessage(R.string.import_playlist_message) .setMessage(R.string.import_playlist_message)
.setPositiveButton(R.string.import_label) { _, _ -> .setPositiveButton(R.string.import_label) { _, _ ->
libraryViewModel.importPlaylists() try {
libraryViewModel.importPlaylists()
} catch (e: Exception) {
e.printStackTrace()
}
} }
.create() .create()
.colorButtons() .colorButtons()

View file

@ -14,14 +14,16 @@
*/ */
package code.name.monkey.retromusic.extensions package code.name.monkey.retromusic.extensions
import android.R
import android.app.Activity import android.app.Activity
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DimenRes import androidx.annotation.DimenRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) { fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) {
//toolbar.setBackgroundColor(surfaceColor())
ToolbarContentTintHelper.colorBackButton(toolbar) ToolbarContentTintHelper.colorBackButton(toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
} }
@ -38,4 +40,6 @@ inline fun <reified T : Any> Activity.extraNotNull(key: String, default: T? = nu
fun Activity.dip(@DimenRes id: Int): Int { fun Activity.dip(@DimenRes id: Int): Int {
return resources.getDimensionPixelSize(id) return resources.getDimensionPixelSize(id)
} }
inline val Activity.rootView: View get() = findViewById<ViewGroup>(R.id.content).getChildAt(0)

View file

@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isGone
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.appthemehelper.ATH import code.name.monkey.appthemehelper.ATH
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
@ -46,7 +47,6 @@ fun AppCompatActivity.exitFullscreen() {
} }
} }
fun AppCompatActivity.hideStatusBar() { fun AppCompatActivity.hideStatusBar() {
hideStatusBar(PreferenceUtil.isFullScreenMode) hideStatusBar(PreferenceUtil.isFullScreenMode)
} }
@ -54,16 +54,25 @@ fun AppCompatActivity.hideStatusBar() {
private fun AppCompatActivity.hideStatusBar(fullscreen: Boolean) { private fun AppCompatActivity.hideStatusBar(fullscreen: Boolean) {
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar) val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
if (statusBar != null) { if (statusBar != null) {
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE statusBar.isGone = fullscreen
} }
} }
fun AppCompatActivity.setDrawBehindSystemBars() { fun AppCompatActivity.setDrawBehindSystemBars() {
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.TRANSPARENT if (VersionUtils.hasOreo()) {
window.navigationBarColor = Color.TRANSPARENT if (VersionUtils.hasQ()) {
if (VersionUtils.hasQ()) { window.isNavigationBarContrastEnforced = false
window.isNavigationBarContrastEnforced = false }
setNavigationBarColor(Color.TRANSPARENT)
setStatusBarColor(Color.TRANSPARENT)
} else {
setNavigationBarColorPreOreo(surfaceColor())
if (VersionUtils.hasMarshmallow()) {
setStatusBarColor(Color.TRANSPARENT)
} else {
setStatusBarColor(surfaceColor())
}
} }
} }
@ -102,7 +111,11 @@ fun AppCompatActivity.setLightStatusBarAuto(bgColor: Int) {
} }
fun AppCompatActivity.setLightNavigationBar(enabled: Boolean) { fun AppCompatActivity.setLightNavigationBar(enabled: Boolean) {
ATH.setLightNavigationBar(this, enabled) ATH.setLightNavigationBar(this, enabled)
}
fun AppCompatActivity.setLightNavigationBarAuto(bgColor: Int) {
setLightNavigationBar(ColorUtil.isColorLight(bgColor))
} }
@ -136,4 +149,32 @@ fun AppCompatActivity.setStatusBarColorAuto() {
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
setStatusBarColor(ATHUtil.resolveColor(this, R.attr.colorSurface)) setStatusBarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
setLightStatusBarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface)) setLightStatusBarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
}
fun AppCompatActivity.setNavigationBarColor(color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
window.navigationBarColor = color
} else {
window.navigationBarColor = ColorUtil.darkenColor(color)
}
setLightNavigationBarAuto(color)
}
fun AppCompatActivity.setNavigationBarColorPreOreo(color: Int) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
window.navigationBarColor = ColorUtil.darkenColor(color)
}
}
fun AppCompatActivity.setStatusBarColorPreMarshmallow(color: Int) {
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
if (statusBar != null) {
statusBar.setBackgroundColor(
ColorUtil.darkenColor(
color
)
)
} else {
window.statusBarColor = ColorUtil.darkenColor(color)
}
} }

View file

@ -20,7 +20,7 @@ import android.database.Cursor
internal fun Cursor.getInt(columnName: String): Int { internal fun Cursor.getInt(columnName: String): Int {
try { try {
return this.getInt(this.getColumnIndex(columnName)) return getInt(getColumnIndexOrThrow(columnName))
} catch (ex: Throwable) { } catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex) throw IllegalStateException("invalid column $columnName", ex)
} }
@ -28,7 +28,7 @@ internal fun Cursor.getInt(columnName: String): Int {
internal fun Cursor.getLong(columnName: String): Long { internal fun Cursor.getLong(columnName: String): Long {
try { try {
return this.getLong(this.getColumnIndex(columnName)) return getLong(getColumnIndexOrThrow(columnName))
} catch (ex: Throwable) { } catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex) throw IllegalStateException("invalid column $columnName", ex)
} }
@ -36,7 +36,7 @@ internal fun Cursor.getLong(columnName: String): Long {
internal fun Cursor.getString(columnName: String): String { internal fun Cursor.getString(columnName: String): String {
try { try {
return this.getString(this.getColumnIndex(columnName)) return getString(getColumnIndexOrThrow(columnName))
} catch (ex: Throwable) { } catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex) throw IllegalStateException("invalid column $columnName", ex)
} }
@ -44,7 +44,7 @@ internal fun Cursor.getString(columnName: String): String {
internal fun Cursor.getStringOrNull(columnName: String): String? { internal fun Cursor.getStringOrNull(columnName: String): String? {
try { try {
return this.getString(this.getColumnIndex(columnName)) return getString(getColumnIndexOrThrow(columnName))
} catch (ex: Throwable) { } catch (ex: Throwable) {
throw IllegalStateException("invalid column $columnName", ex) throw IllegalStateException("invalid column $columnName", ex)
} }

View file

@ -66,7 +66,6 @@ val FragmentManager.currentNavigationFragment: Fragment?
fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? { fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? {
val navHostFragment: NavHostFragment = val navHostFragment: NavHostFragment =
supportFragmentManager.findFragmentById(navHostId) as NavHostFragment supportFragmentManager.findFragmentById(navHostId) as NavHostFragment
navHostFragment.targetFragment
return navHostFragment.childFragmentManager.fragments.firstOrNull() return navHostFragment.childFragmentManager.fragments.firstOrNull()
} }

View file

@ -2,7 +2,6 @@ package code.name.monkey.retromusic.extensions
import android.net.Uri import android.net.Uri
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.fragment.app.Fragment
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import org.jaudiotagger.audio.AudioFileIO import org.jaudiotagger.audio.AudioFileIO

View file

@ -24,7 +24,6 @@ import androidx.recyclerview.widget.GridLayoutManager
import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.extensions.navigate
import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.GridStyle import code.name.monkey.retromusic.fragments.GridStyle
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType

View file

@ -7,7 +7,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog import androidx.core.net.toUri
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
@ -49,7 +49,9 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) { val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
it?.let { it?.let {
backupViewModel.restoreBackup(requireActivity(), requireContext().contentResolver.openInputStream(it)) startActivity(Intent(context, RestoreActivity::class.java).apply {
data = it
})
} }
} }
} }
@ -103,17 +105,11 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
} }
override fun onBackupClicked(file: File) { override fun onBackupClicked(file: File) {
AlertDialog.Builder(requireContext()) lifecycleScope.launch {
.setTitle(R.string.restore) startActivity(Intent(context, RestoreActivity::class.java).apply {
.setMessage(R.string.restore_message) data = file.toUri()
.setPositiveButton(R.string.restore) { _, _ -> })
lifecycleScope.launch { }
backupViewModel.restoreBackup(requireActivity(), file.inputStream())
}
}
.setNegativeButton(android.R.string.cancel, null)
.create()
.show()
} }
@SuppressLint("CheckResult") @SuppressLint("CheckResult")

View file

@ -5,6 +5,8 @@ import android.content.Intent
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.helper.BackupContent
import code.name.monkey.retromusic.helper.BackupHelper import code.name.monkey.retromusic.helper.BackupHelper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -25,12 +27,12 @@ class BackupViewModel : ViewModel() {
} }
} }
suspend fun restoreBackup(activity: Activity, inputStream: InputStream?) { suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List<BackupContent>) {
BackupHelper.restoreBackup(activity, inputStream) BackupHelper.restoreBackup(activity, inputStream, contents)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val intent = Intent( val intent = Intent(
activity, activity,
activity::class.java MainActivity::class.java
) )
activity.startActivity(intent) activity.startActivity(intent)
exitProcess(0) exitProcess(0)

View file

@ -1,12 +1,89 @@
package code.name.monkey.retromusic.fragments.backup package code.name.monkey.retromusic.fragments.backup
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.databinding.ActivityRestoreBinding
import code.name.monkey.retromusic.helper.BackupContent
import code.name.monkey.retromusic.helper.BackupContent.*
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.theme.ThemeManager
import com.google.android.material.color.DynamicColors
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class RestoreActivity : AppCompatActivity() { class RestoreActivity : AppCompatActivity() {
lateinit var binding: ActivityRestoreBinding
private val backupViewModel: BackupViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
updateTheme()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_restore) binding = ActivityRestoreBinding.inflate(layoutInflater)
setContentView(binding.root)
val backupUri = intent?.data
binding.backupName.setText(getFileName(backupUri))
binding.cancelButton.setOnClickListener {
finish()
}
binding.restoreButton.setOnClickListener {
val backupContents = mutableListOf<BackupContent>()
if (binding.checkSettings.isChecked) backupContents.add(SETTINGS)
if (binding.checkQueue.isChecked) backupContents.add(QUEUE)
if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS)
if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES)
if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES)
lifecycleScope.launch(Dispatchers.IO) {
if (backupUri != null) {
contentResolver.openInputStream(backupUri)?.use {
backupViewModel.restoreBackup(this@RestoreActivity, it, backupContents)
}
}
}
}
}
private fun updateTheme() {
AppCompatDelegate.setDefaultNightMode(ThemeManager.getNightMode(this))
// Apply dynamic colors to activity if enabled
if (PreferenceUtil.materialYou) {
DynamicColors.applyIfAvailable(
this,
com.google.android.material.R.style.ThemeOverlay_Material3_DynamicColors_DayNight
)
}
}
private fun getFileName(uri: Uri?): String? {
when (uri?.scheme) {
"file" -> {
return uri.lastPathSegment
}
"content" -> {
val proj = arrayOf(MediaStore.Images.Media.TITLE)
contentResolver.query(
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}, proj, null, null, null
)?.use { cursor ->
if (cursor.count != 0) {
val columnIndex: Int =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE)
cursor.moveToFirst()
return cursor.getString(columnIndex)
}
}
}
}
return "Backup"
} }
} }

View file

@ -21,9 +21,9 @@ import android.os.Environment
import android.text.Html import android.text.Html
import android.view.* import android.view.*
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.appcompat.widget.PopupMenu
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.PopupMenu
import androidx.loader.app.LoaderManager import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader import androidx.loader.content.Loader
import androidx.navigation.Navigation.findNavController import androidx.navigation.Navigation.findNavController

View file

@ -27,12 +27,10 @@ import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.retromusic.EXTRA_GENRE import code.name.monkey.retromusic.EXTRA_GENRE
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.GenreAdapter import code.name.monkey.retromusic.adapter.GenreAdapter
import code.name.monkey.retromusic.extensions.navigate
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.interfaces.IGenreClickListener import code.name.monkey.retromusic.interfaces.IGenreClickListener
import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis

View file

@ -23,6 +23,7 @@ import android.view.View
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.view.doOnLayout
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -87,6 +88,21 @@ class HomeFragment :
remove() remove()
mainActivity.finish() mainActivity.finish()
} }
view.doOnLayout {
adjustPlaylistButtons()
}
}
private fun adjustPlaylistButtons() {
val buttons =
listOf(binding.history, binding.lastAdded, binding.topPlayed, binding.actionShuffle)
buttons.maxOf { it.lineCount }.let { maxLineCount->
buttons.forEach { button ->
// Set the highest line count to every button for consistency
button.setLines(maxLineCount)
}
}
} }
private fun setupListeners() { private fun setupListeners() {

View file

@ -64,14 +64,6 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false) returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
} }
} }
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(binding.toolbar)
binding.progressIndicator.hide() binding.progressIndicator.hide()
when (args.type) { when (args.type) {
@ -92,6 +84,10 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
binding.recyclerView.updatePadding(bottom = height.toInt()) binding.recyclerView.updatePadding(bottom = height.toInt())
} }
}) })
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }
} }
private fun lastAddedSongs() { private fun lastAddedSongs() {
@ -104,6 +100,7 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = songAdapter adapter = songAdapter
layoutManager = linearLayoutManager() layoutManager = linearLayoutManager()
scheduleLayoutAnimation()
} }
libraryViewModel.recentSongs().observe(viewLifecycleOwner, { songs -> libraryViewModel.recentSongs().observe(viewLifecycleOwner, { songs ->
songAdapter.swapDataSet(songs) songAdapter.swapDataSet(songs)

View file

@ -134,12 +134,11 @@ class UserInfoFragment : Fragment() {
private fun loadProfile() { private fun loadProfile() {
binding.bannerImage.let { binding.bannerImage.let {
GlideApp.with(this) GlideApp.with(this)
.asBitmap()
.load(RetroGlideExtension.getBannerModel()) .load(RetroGlideExtension.getBannerModel())
.profileBannerOptions(RetroGlideExtension.getBannerModel()) .profileBannerOptions(RetroGlideExtension.getBannerModel())
.into(it) .into(it)
} }
GlideApp.with(this).asBitmap() GlideApp.with(this)
.load(RetroGlideExtension.getUserModel()) .load(RetroGlideExtension.getUserModel())
.userProfileOptions(RetroGlideExtension.getUserModel()) .userProfileOptions(RetroGlideExtension.getUserModel())
.into(binding.userImage) .into(binding.userImage)

View file

@ -14,42 +14,36 @@
*/ */
package code.name.monkey.retromusic.fragments.player package code.name.monkey.retromusic.fragments.player
import android.annotation.SuppressLint
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.FrameLayout import androidx.core.view.isInvisible
import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.SHOW_LYRICS import code.name.monkey.retromusic.SHOW_LYRICS
import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter
import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter.AlbumCoverFragment import code.name.monkey.retromusic.adapter.album.AlbumCoverPagerAdapter.AlbumCoverFragment
import code.name.monkey.retromusic.databinding.FragmentPlayerAlbumCoverBinding import code.name.monkey.retromusic.databinding.FragmentPlayerAlbumCoverBinding
import code.name.monkey.retromusic.extensions.isColorLight
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToLyrics import code.name.monkey.retromusic.fragments.base.goToLyrics
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics import code.name.monkey.retromusic.lyrics.CoverLrcView
import code.name.monkey.retromusic.model.lyrics.Lyrics import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.transform.CarousalPagerTransformer import code.name.monkey.retromusic.transform.CarousalPagerTransformer
import code.name.monkey.retromusic.transform.ParallaxPagerTransformer import code.name.monkey.retromusic.transform.ParallaxPagerTransformer
import code.name.monkey.retromusic.util.LyricUtil import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.audio.exceptions.CannotReadException
import org.jaudiotagger.tag.FieldKey
import java.io.File
import java.io.FileNotFoundException
class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover), class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover),
ViewPager.OnPageChangeListener, MusicProgressViewUpdateHelper.Callback, ViewPager.OnPageChangeListener, MusicProgressViewUpdateHelper.Callback,
@ -70,9 +64,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} }
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
private val lyricsLayout: FrameLayout get() = binding.playerLyrics private val lrcView: CoverLrcView get() = binding.lyricsView
private val lyricsLine1: TextView get() = binding.playerLyricsLine1
private val lyricsLine2: TextView get() = binding.playerLyricsLine2
var lyrics: Lyrics? = null var lyrics: Lyrics? = null
@ -82,102 +74,28 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
} }
private fun updateLyrics() { private fun updateLyrics() {
lyrics = null binding.lyricsView.setLabel("Empty")
lifecycleScope.launch(Dispatchers.IO) { val song = MusicPlayerRemote.currentSong
val song = MusicPlayerRemote.currentSong when {
val lyrics = try { LyricUtil.isLrcOriginalFileExist(song.data) -> {
var lrcFile: File? = null LyricUtil.getLocalLyricOriginalFile(song.data)
if (LyricUtil.isLrcOriginalFileExist(song.data)) { ?.let { binding.lyricsView.loadLrc(it) }
lrcFile = LyricUtil.getLocalLyricOriginalFile(song.data)
} else if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
lrcFile = LyricUtil.getLocalLyricFile(song.title, song.artistName)
}
val data: String = LyricUtil.getStringFromLrc(lrcFile)
if (!TextUtils.isEmpty(data)) {
Lyrics.parse(song, data)
} else {
// Get Embedded Lyrics and check if they are Synchronized
val embeddedLyrics = try{
AudioFileIO.read(File(song.data)).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
} catch(e: Exception){
null
}
if (AbsSynchronizedLyrics.isSynchronized(embeddedLyrics)) {
Lyrics.parse(song, embeddedLyrics)
} else {
null
}
}
} catch (err: FileNotFoundException) {
null
} catch (e: CannotReadException){
null
} }
withContext(Dispatchers.Main) { LyricUtil.isLrcFileExist(song.title, song.artistName) -> {
this@PlayerAlbumCoverFragment.lyrics = lyrics LyricUtil.getLocalLyricFile(song.title, song.artistName)
?.let { binding.lyricsView.loadLrc(it) }
}
else -> {
binding.lyricsView.reset()
} }
} }
} }
override fun onUpdateProgressViews(progress: Int, total: Int) { override fun onUpdateProgressViews(progress: Int, total: Int) {
if (_binding == null) return binding.lyricsView.updateTime(progress.toLong())
if (!isLyricsLayoutVisible()) {
hideLyricsLayout()
return
}
if (lyrics !is AbsSynchronizedLyrics) return
val synchronizedLyrics = lyrics as AbsSynchronizedLyrics
lyricsLayout.visibility = View.VISIBLE
lyricsLayout.alpha = 1f
val oldLine = lyricsLine2.text.toString()
val line = synchronizedLyrics.getLine(progress)
if (oldLine != line || oldLine.isEmpty()) {
lyricsLine1.text = oldLine
lyricsLine2.text = line
lyricsLine1.visibility = View.VISIBLE
lyricsLine2.visibility = View.VISIBLE
lyricsLine2.measure(
View.MeasureSpec.makeMeasureSpec(
lyricsLine2.measuredWidth,
View.MeasureSpec.EXACTLY
),
View.MeasureSpec.UNSPECIFIED
)
val h: Float = lyricsLine2.measuredHeight.toFloat()
lyricsLine1.alpha = 1f
lyricsLine1.translationY = 0f
lyricsLine1.animate().alpha(0f).translationY(-h).duration =
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
lyricsLine2.alpha = 0f
lyricsLine2.translationY = h
lyricsLine2.animate().alpha(1f).translationY(0f).duration =
AbsPlayerFragment.VISIBILITY_ANIM_DURATION
}
}
private fun isLyricsLayoutVisible(): Boolean {
return lyrics != null && lyrics!!.isSynchronized && lyrics!!.isValid
}
private fun hideLyricsLayout() {
lyricsLayout.animate().alpha(0f).setDuration(AbsPlayerFragment.VISIBILITY_ANIM_DURATION)
.withEndAction {
if (_binding == null) return@withEndAction
lyricsLayout.visibility = View.GONE
lyricsLine1.text = null
lyricsLine2.text = null
}
} }
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentPlayerAlbumCoverBinding.bind(view) _binding = FragmentPlayerAlbumCoverBinding.bind(view)
@ -210,14 +128,25 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this, 500, 1000) progressViewUpdateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
// Don't show lyrics container for below conditions // Don't show lyrics container for below conditions
if (!(nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics)) { if (!(nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics)) {
lyricsLayout.isVisible = false lrcView.isVisible = false
viewPager.isInvisible = false
progressViewUpdateHelper?.stop() progressViewUpdateHelper?.stop()
} else { } else {
lyricsLayout.isVisible = true lrcView.isVisible = true
viewPager.isInvisible = true
progressViewUpdateHelper?.start() progressViewUpdateHelper?.start()
} }
lrcView.apply {
setDraggable(true, object : CoverLrcView.OnPlayClickListener {
override fun onPlayClick(time: Long): Boolean {
MusicPlayerRemote.seekTo(time.toInt())
MusicPlayerRemote.resumePlaying()
return true
}
})
}
// Go to lyrics activity when clicked lyrics // Go to lyrics activity when clicked lyrics
binding.playerLyricsLine2.setOnClickListener { lrcView.setOnClickListener {
goToLyrics(requireActivity()) goToLyrics(requireActivity())
} }
} }
@ -227,10 +156,12 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
val nps = PreferenceUtil.nowPlayingScreen val nps = PreferenceUtil.nowPlayingScreen
// Don't show lyrics container for below conditions // Don't show lyrics container for below conditions
if (nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics) { if (nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics) {
lyricsLayout.isVisible = false lrcView.isVisible = false
viewPager.isInvisible = false
progressViewUpdateHelper?.stop() progressViewUpdateHelper?.stop()
} else { } else {
lyricsLayout.isVisible = true lrcView.isVisible = true
viewPager.isInvisible = true
progressViewUpdateHelper?.start() progressViewUpdateHelper?.start()
} }
PreferenceManager.getDefaultSharedPreferences(requireContext()) PreferenceManager.getDefaultSharedPreferences(requireContext())
@ -266,30 +197,42 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
val nps = PreferenceUtil.nowPlayingScreen val nps = PreferenceUtil.nowPlayingScreen
// Don't show lyrics container for below conditions // Don't show lyrics container for below conditions
if (!(nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics)) { if (!(nps == Circle || nps == Peak || nps == Tiny || !PreferenceUtil.showLyrics)) {
lyricsLayout.isVisible = false lrcView.isVisible = true
progressViewUpdateHelper?.stop() viewPager.isInvisible = true
} else {
lyricsLayout.isVisible = true
progressViewUpdateHelper?.start() progressViewUpdateHelper?.start()
lyricsLayout.animate().alpha(1f).duration = lrcView.animate().alpha(1f).duration =
AbsPlayerFragment.VISIBILITY_ANIM_DURATION AbsPlayerFragment.VISIBILITY_ANIM_DURATION
binding.playerLyrics.isVisible = true } else {
lrcView.isVisible = false
viewPager.isInvisible = false
progressViewUpdateHelper?.stop()
} }
} else { } else {
lrcView.isVisible = false
viewPager.isInvisible = false
progressViewUpdateHelper?.stop() progressViewUpdateHelper?.stop()
lyricsLayout.animate().alpha(0f)
.setDuration(AbsPlayerFragment.VISIBILITY_ANIM_DURATION)
.withEndAction {
if (_binding != null) {
binding.playerLyrics.isVisible = false
lyricsLine1.text = null
lyricsLine2.text = null
}
}
} }
} }
} }
private fun setLRCViewColors(backgroundColor: Int) {
val primaryColor = MaterialValueHelper.getPrimaryTextColor(
requireContext(),
backgroundColor.isColorLight
)
val secondaryColor = MaterialValueHelper.getSecondaryDisabledTextColor(
requireContext(),
backgroundColor.isColorLight
)
lrcView.apply {
setCurrentColor(primaryColor)
setTimeTextColor(primaryColor)
setTimelineColor(primaryColor)
setNormalColor(secondaryColor)
setTimelineTextColor(primaryColor)
}
}
private fun updatePlayingQueue() { private fun updatePlayingQueue() {
binding.viewPager.apply { binding.viewPager.apply {
adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue) adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue)
@ -321,6 +264,18 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
private fun notifyColorChange(color: MediaNotificationProcessor) { private fun notifyColorChange(color: MediaNotificationProcessor) {
callbacks?.onColorChanged(color) callbacks?.onColorChanged(color)
setLRCViewColors(
when (PreferenceUtil.nowPlayingScreen) {
Adaptive, Fit, Plain, Simple -> surfaceColor()
Flat, Normal -> if (PreferenceUtil.isAdaptiveColor) {
color.backgroundColor
} else {
surfaceColor()
}
Color ->color.backgroundColor
Blur -> Color.BLACK
else -> color.backgroundColor
})
} }
fun setCallbacks(listener: Callbacks) { fun setCallbacks(listener: Callbacks) {

View file

@ -47,7 +47,7 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) {
_binding = FragmentAdaptivePlayerBinding.bind(view) _binding = FragmentAdaptivePlayerBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
binding.root.drawAboveSystemBars() binding.playbackControlsFragment.drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -16,6 +16,8 @@ package code.name.monkey.retromusic.fragments.player.blur
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
@ -27,18 +29,18 @@ import code.name.monkey.retromusic.databinding.FragmentBlurBinding
import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.glide.BlurTransformation import code.name.monkey.retromusic.glide.*
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur), class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
private var lastRequest: GlideRequest<Drawable>? = null
override fun playerToolbar(): Toolbar { override fun playerToolbar(): Toolbar {
return binding.playerToolbar return binding.playerToolbar
} }
@ -111,23 +113,20 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
get() = lastColor get() = lastColor
private fun updateBlur() { private fun updateBlur() {
binding.colorBackground.clearColorFilter() // https://github.com/bumptech/glide/issues/527#issuecomment-148840717
GlideApp.with(requireActivity()).asBitmapPalette() GlideApp.with(this)
.songCoverOptions(MusicPlayerRemote.currentSong)
.load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong)) .load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong))
.dontAnimate() .simpleSongCoverOptions(MusicPlayerRemote.currentSong)
.transform( .transform(
BlurTransformation.Builder(requireContext()) BlurTransformation.Builder(requireContext()).blurRadius(blurAmount.toFloat())
.blurRadius(blurAmount.toFloat())
.build() .build()
) ).thumbnail(lastRequest)
.into(object : RetroMusicColoredTarget(binding.colorBackground) { .error(GlideApp.with(this).load(ColorDrawable(Color.DKGRAY)).fitCenter())
override fun onColorReady(colors: MediaNotificationProcessor) { .also {
if (colors.backgroundColor == defaultFooterColor) { lastRequest = it.clone()
binding.colorBackground.setColorFilter(colors.backgroundColor) it.crossfadeListener()
} .into(binding.colorBackground)
} }
})
} }
override fun onServiceConnected() { override fun onServiceConnected() {

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.player.cardblur
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
@ -28,10 +29,7 @@ import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
import code.name.monkey.retromusic.glide.BlurTransformation import code.name.monkey.retromusic.glide.*
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
@ -50,6 +48,7 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
private var _binding: FragmentCardBlurPlayerBinding? = null private var _binding: FragmentCardBlurPlayerBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private var lastRequest: GlideRequest<Drawable>? = null
override fun onShow() { override fun onShow() {
playbackControlsFragment.show() playbackControlsFragment.show()
@ -136,22 +135,19 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
} }
private fun updateBlur() { private fun updateBlur() {
binding.colorBackground.clearColorFilter() // https://github.com/bumptech/glide/issues/527#issuecomment-148840717
GlideApp.with(requireActivity()).asBitmapPalette() GlideApp.with(this)
.songCoverOptions(MusicPlayerRemote.currentSong)
.load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong)) .load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong))
.dontAnimate() .simpleSongCoverOptions(MusicPlayerRemote.currentSong)
.transform( .transform(
BlurTransformation.Builder(requireContext()).blurRadius(blurAmount.toFloat()) BlurTransformation.Builder(requireContext()).blurRadius(blurAmount.toFloat())
.build() .build()
) )
.into(object : RetroMusicColoredTarget(binding.colorBackground) { .thumbnail(lastRequest).also {
override fun onColorReady(colors: MediaNotificationProcessor) { lastRequest = it.clone()
if (colors.backgroundColor == defaultFooterColor) { it.crossfadeListener()
binding.colorBackground.setColorFilter(colors.backgroundColor) .into(binding.colorBackground)
} }
}
})
} }
override fun onResume() { override fun onResume() {

View file

@ -18,19 +18,19 @@ import android.animation.ObjectAnimator
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.media.AudioManager import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.SeekBar import android.widget.SeekBar
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.*
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding
import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.extensions.*
@ -38,6 +38,7 @@ import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.glide.*
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
@ -47,10 +48,9 @@ import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ViewUtil import code.name.monkey.retromusic.util.ViewUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import code.name.monkey.retromusic.views.SeekArc
import code.name.monkey.retromusic.views.SeekArc.OnSeekArcChangeListener
import code.name.monkey.retromusic.volume.AudioVolumeObserver import code.name.monkey.retromusic.volume.AudioVolumeObserver
import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener
import me.tankery.lib.circularseekbar.CircularSeekBar
/** /**
* Created by hemanths on 2020-01-06. * Created by hemanths on 2020-01-06.
@ -58,7 +58,7 @@ import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener
class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player), Callback, class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player), Callback,
OnAudioVolumeChangedListener, OnAudioVolumeChangedListener,
OnSeekArcChangeListener { CircularSeekBar.OnCircularSeekBarChangeListener {
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var audioVolumeObserver: AudioVolumeObserver? = null private var audioVolumeObserver: AudioVolumeObserver? = null
@ -69,6 +69,9 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
private var _binding: FragmentCirclePlayerBinding? = null private var _binding: FragmentCirclePlayerBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private var rotateAnimator: ObjectAnimator? = null
private var lastRequest: GlideRequest<Drawable>? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
@ -116,11 +119,17 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
ThemeStore.accentColor(requireContext()), ThemeStore.accentColor(requireContext()),
false false
) )
binding.volumeSeekBar.progressColor = accentColor() binding.volumeSeekBar.circleProgressColor = accentColor()
binding.volumeSeekBar.arcColor = ColorUtil.withAlpha(accentColor(), 0.25f) binding.volumeSeekBar.circleColor = ColorUtil.withAlpha(accentColor(), 0.25f)
setUpPlayPauseFab() setUpPlayPauseFab()
setUpPrevNext() setUpPrevNext()
setUpPlayerToolbar() setUpPlayerToolbar()
binding.albumCoverOverlay.background = ColorDrawable(
MaterialValueHelper.getPrimaryTextColor(
requireContext(),
accentColor().isColorLight
)
)
} }
private fun setUpPrevNext() { private fun setUpPrevNext() {
@ -144,6 +153,17 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler()) binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
} }
private fun setupRotateAnimation() {
rotateAnimator = ObjectAnimator.ofFloat(binding.albumCover, View.ROTATION, 360F).apply {
interpolator = LinearInterpolator()
repeatCount = Animation.INFINITE
duration = 10000
if (MusicPlayerRemote.isPlaying) {
start()
}
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
progressViewUpdateHelper.start() progressViewUpdateHelper.start()
@ -153,11 +173,11 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this) audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this)
val audioManager = audioManager val audioManager = audioManager
if (audioManager != null) { binding.volumeSeekBar.max =
binding.volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat()
binding.volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) binding.volumeSeekBar.progress =
} audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).toFloat()
binding.volumeSeekBar.setOnSeekArcChangeListener(this) binding.volumeSeekBar.setOnSeekBarChangeListener(this)
} }
override fun onPause() { override fun onPause() {
@ -191,6 +211,11 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
override fun onPlayStateChanged() { override fun onPlayStateChanged() {
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
if (MusicPlayerRemote.isPlaying) {
if (rotateAnimator?.isStarted == true) rotateAnimator?.resume() else rotateAnimator?.start()
} else {
rotateAnimator?.pause()
}
} }
override fun onPlayingMetaChanged() { override fun onPlayingMetaChanged() {
@ -202,6 +227,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
super.onServiceConnected() super.onServiceConnected()
updateSong() updateSong()
updatePlayPauseDrawableState() updatePlayPauseDrawableState()
setupRotateAnimation()
} }
private fun updateSong() { private fun updateSong() {
@ -215,6 +241,16 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
} else { } else {
binding.songInfo.hide() binding.songInfo.hide()
} }
GlideApp.with(this)
.load(RetroGlideExtension.getSongModel(MusicPlayerRemote.currentSong))
.simpleSongCoverOptions(MusicPlayerRemote.currentSong)
.thumbnail(lastRequest)
.error(GlideApp.with(this).load(R.drawable.default_audio_art).fitCenter())
.fitCenter().also {
lastRequest = it.clone()
it.crossfadeListener()
.into(binding.albumCover)
}
} }
private fun updatePlayPauseDrawableState() { private fun updatePlayPauseDrawableState() {
@ -225,8 +261,8 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
} }
override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) { override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) {
_binding?.volumeSeekBar?.max = maxVolume _binding?.volumeSeekBar?.max = maxVolume.toFloat()
_binding?.volumeSeekBar?.progress = currentVolume _binding?.volumeSeekBar?.progress = currentVolume.toFloat()
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -237,15 +273,16 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
_binding = null _binding = null
} }
override fun onProgressChanged(seekArc: SeekArc?, progress: Int, fromUser: Boolean) {
override fun onProgressChanged(seekBar: CircularSeekBar?, progress: Float, fromUser: Boolean) {
val audioManager = audioManager val audioManager = audioManager
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0) audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress.toInt(), 0)
} }
override fun onStartTrackingTouch(seekArc: SeekArc?) { override fun onStartTrackingTouch(seekBar: CircularSeekBar?) {
} }
override fun onStopTrackingTouch(seekArc: SeekArc?) { override fun onStopTrackingTouch(seekBar: CircularSeekBar?) {
} }
fun setUpProgressSlider() { fun setUpProgressSlider() {

View file

@ -26,14 +26,13 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import androidx.appcompat.widget.PopupMenu
import android.widget.SeekBar import android.widget.SeekBar
import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding import code.name.monkey.retromusic.databinding.FragmentFullPlayerControlsBinding
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongEntity import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.applyColor
import code.name.monkey.retromusic.extensions.getSongInfo import code.name.monkey.retromusic.extensions.getSongInfo

View file

@ -23,9 +23,9 @@ import android.graphics.drawable.AnimatedVectorDrawable
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import androidx.appcompat.widget.PopupMenu
import android.widget.SeekBar import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
@ -39,9 +39,6 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment

View file

@ -281,12 +281,13 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player),
return gestureDetector.onTouchEvent(event) return gestureDetector.onTouchEvent(event)
} }
@Suppress("Deprecation")
private fun vibrate() { private fun vibrate() {
val v = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? val v = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v!!.vibrate(VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)) v?.vibrate(VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE))
} else { } else {
v!!.vibrate(10) v?.vibrate(10)
} }
} }
} }

View file

@ -70,7 +70,10 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
setupRecyclerView() setupRecyclerView()
binding.voiceSearch.setOnClickListener { startMicSearch() } binding.voiceSearch.setOnClickListener { startMicSearch() }
binding.clearText.setOnClickListener { binding.searchView.clearText() } binding.clearText.setOnClickListener {
binding.searchView.clearText()
searchAdapter.swapDataSet(listOf())
}
binding.searchView.apply { binding.searchView.apply {
addTextChangedListener(this@SearchFragment) addTextChangedListener(this@SearchFragment)
focusAndShowKeyboard() focusAndShowKeyboard()

View file

@ -16,6 +16,7 @@ package code.name.monkey.retromusic.fragments.settings
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
@ -26,6 +27,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat
import code.name.monkey.retromusic.activities.OnThemeChangedListener import code.name.monkey.retromusic.activities.OnThemeChangedListener
import code.name.monkey.retromusic.extensions.rootView
import code.name.monkey.retromusic.extensions.safeGetBottomInsets import code.name.monkey.retromusic.extensions.safeGetBottomInsets
import code.name.monkey.retromusic.preferences.* import code.name.monkey.retromusic.preferences.*
import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.NavigationUtil
@ -67,11 +69,15 @@ abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setDivider(ColorDrawable(Color.TRANSPARENT)) setDivider(ColorDrawable(Color.TRANSPARENT))
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
listView.overScrollMode = View.OVER_SCROLL_NEVER
}
// CollapsingToolbarLayout consumes insets and insets are not passed to child views // CollapsingToolbarLayout consumes insets and insets are not passed to child views
// So we get insets from decor view // So we get insets from decor view
// https://github.com/material-components/material-components-android/issues/1310 // https://github.com/material-components/material-components-android/issues/1310
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
requireActivity().window.decorView requireActivity().rootView
) { _, insets -> ) { _, insets ->
listView.updatePadding(bottom = insets.safeGetBottomInsets()) listView.updatePadding(bottom = insets.safeGetBottomInsets())
insets insets

View file

@ -28,6 +28,7 @@ import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentMainSettingsBinding import code.name.monkey.retromusic.databinding.FragmentMainSettingsBinding
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.rootView
import code.name.monkey.retromusic.extensions.safeGetBottomInsets import code.name.monkey.retromusic.extensions.safeGetBottomInsets
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.NavigationUtil
@ -89,9 +90,9 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
} }
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
requireActivity().window.decorView requireActivity().rootView
) { _, insets -> ) { _, insets ->
binding.container.updatePadding(bottom = insets.safeGetBottomInsets()) _binding?.container?.updatePadding(bottom = insets.safeGetBottomInsets())
insets insets
} }
} }

View file

@ -23,9 +23,15 @@ import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.annotation.GlideExtension import com.bumptech.glide.annotation.GlideExtension
import com.bumptech.glide.annotation.GlideOption import com.bumptech.glide.annotation.GlideOption
import com.bumptech.glide.annotation.GlideType import com.bumptech.glide.annotation.GlideType
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.Key import com.bumptech.glide.load.Key
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.BaseRequestOptions import com.bumptech.glide.request.BaseRequestOptions
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.DrawableCrossFadeFactory
import com.bumptech.glide.request.transition.Transition
import com.bumptech.glide.signature.MediaStoreSignature import com.bumptech.glide.signature.MediaStoreSignature
import java.io.File import java.io.File
@ -116,6 +122,16 @@ object RetroGlideExtension {
.signature(createSignature(song)) .signature(createSignature(song))
} }
@JvmStatic
@GlideOption
fun simpleSongCoverOptions(
baseRequestOptions: BaseRequestOptions<*>,
song: Song
): BaseRequestOptions<*> {
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
.signature(createSignature(song))
}
@JvmStatic @JvmStatic
@GlideOption @GlideOption
fun albumCoverOptions( fun albumCoverOptions(
@ -194,4 +210,33 @@ object RetroGlideExtension {
fun <TranscodeType> getDefaultTransition(): GenericTransitionOptions<TranscodeType> { fun <TranscodeType> getDefaultTransition(): GenericTransitionOptions<TranscodeType> {
return GenericTransitionOptions<TranscodeType>().transition(DEFAULT_ANIMATION) return GenericTransitionOptions<TranscodeType>().transition(DEFAULT_ANIMATION)
} }
}
// https://github.com/bumptech/glide/issues/527#issuecomment-148840717
fun GlideRequest<Drawable>.crossfadeListener(): GlideRequest<Drawable> {
return listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
return if (isFirstResource) {
false // thumbnail was not shown, do as usual
} else DrawableCrossFadeFactory.Builder()
.setCrossFadeEnabled(true).build()
.build(dataSource, isFirstResource)
.transition(resource, target as Transition.ViewAdapter)
}
})
} }

View file

@ -14,6 +14,7 @@
package code.name.monkey.retromusic.glide.audiocover; package code.name.monkey.retromusic.glide.audiocover;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException; import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException; import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.mp3.MP3File; import org.jaudiotagger.audio.mp3.MP3File;
@ -45,7 +46,7 @@ public class AudioFileCoverUtils {
} }
} }
// If there are any exceptions, we ignore them and continue to the other fallback method // If there are any exceptions, we ignore them and continue to the other fallback method
} catch (ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException ignored) { } catch (ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException | CannotReadException ignored) {
} }
// Method 2: look for album art in external files // Method 2: look for album art in external files

View file

@ -2,9 +2,13 @@ package code.name.monkey.retromusic.helper
import android.content.Context import android.content.Context
import android.os.Environment import android.os.Environment
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.BuildConfig import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.helper.BackupContent.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.* import java.io.*
@ -24,10 +28,11 @@ object BackupHelper {
zipItems.addAll(getSettingsZipItems(context)) zipItems.addAll(getSettingsZipItems(context))
getUserImageZipItems(context)?.let { zipItems.addAll(it) } getUserImageZipItems(context)?.let { zipItems.addAll(it) }
zipItems.addAll(getCustomArtistZipItems(context)) zipItems.addAll(getCustomArtistZipItems(context))
zipItems.addAll(getQueueZipItems(context))
zipAll(zipItems, backupFile) zipAll(zipItems, backupFile)
} }
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) { private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
kotlin.runCatching { kotlin.runCatching {
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out -> ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
@ -42,27 +47,42 @@ object BackupHelper {
} }
} }
}.onFailure { }.onFailure {
it.printStackTrace()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT) Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
.show() .show()
} }
throw Exception(it)
}.onSuccess {
withContext(Dispatchers.Main) {
Toast.makeText(
App.getContext(),
"Backup created successfully",
Toast.LENGTH_SHORT
)
.show()
}
} }
withContext(Dispatchers.Main) {
Toast.makeText(App.getContext(), "Backup created successfully", Toast.LENGTH_SHORT)
.show()
}
} }
}
private fun getDatabaseZipItems(context: Context): List<ZipItem> { private fun getDatabaseZipItems(context: Context): List<ZipItem> {
return context.databaseList().filter { return context.databaseList().filter {
it.endsWith(".db") it.endsWith(".db") && it != queueDatabase
}.map { }.map {
ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it") ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it")
} }
} }
private fun getQueueZipItems(context: Context): List<ZipItem> {
Log.d("RetroMusic", context.getDatabasePath(queueDatabase).absolutePath)
return listOf(
ZipItem(
context.getDatabasePath(queueDatabase).absolutePath,
"$QUEUE_PATH${File.separator}$queueDatabase"
)
)
}
private fun getSettingsZipItems(context: Context): List<ZipItem> { private fun getSettingsZipItems(context: Context): List<ZipItem> {
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/"
return listOf( return listOf(
@ -94,33 +114,47 @@ object BackupHelper {
) )
}?.toList() ?: listOf() }?.toList() ?: listOf()
) )
zipItemList.add( File(sharedPrefPath + File.separator + "custom_artist_image.xml").let {
ZipItem( if (it.exists()) {
sharedPrefPath + File.separator + "custom_artist_image.xml", zipItemList.add(
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml" ZipItem(
) it.absolutePath,
) "$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml"
)
)
}
}
return zipItemList return zipItemList
} }
suspend fun restoreBackup(context: Context, inputStream: InputStream?) { suspend fun restoreBackup(
context: Context,
inputStream: InputStream?,
contents: List<BackupContent>
) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
ZipInputStream(inputStream).use { ZipInputStream(inputStream).use {
var entry = it.nextEntry var entry = it.nextEntry
while (entry != null) { while (entry != null) {
if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry) if (entry.isDatabaseEntry() && contents.contains(PLAYLISTS)) {
if (entry.isPreferenceEntry()) restorePreferences(context, it, entry) restoreDatabase(context, it, entry)
if (entry.isImageEntry()) restoreImages(context, it, entry) } else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) {
if (entry.isCustomArtistImageEntry()) restoreCustomArtistImages( restorePreferences(context, it, entry)
context, } else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) {
it, restoreImages(context, it, entry)
entry
) } else if (entry.isCustomArtistImageEntry() && contents.contains(
if (entry.isCustomArtistPrefEntry()) restoreCustomArtistPrefs( CUSTOM_ARTIST_IMAGES
context, )
it, ) {
entry restoreCustomArtistImages(context, it, entry)
) restoreCustomArtistPrefs(context, it, entry)
} else if (entry.isQueueEntry() && contents.contains(QUEUE)) {
restoreQueueDatabase(context, it, entry)
}
entry = it.nextEntry entry = it.nextEntry
} }
} }
@ -170,6 +204,21 @@ object BackupHelper {
} }
} }
private fun restoreQueueDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) {
putInt("POSITION", 0)
}
val filePath =
context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName()
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
private fun restoreCustomArtistImages( private fun restoreCustomArtistImages(
context: Context, context: Context,
zipIn: ZipInputStream, zipIn: ZipInputStream,
@ -218,10 +267,12 @@ object BackupHelper {
const val BACKUP_EXTENSION = "rmbak" const val BACKUP_EXTENSION = "rmbak"
const val APPEND_EXTENSION = ".$BACKUP_EXTENSION" const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
private const val DATABASES_PATH = "databases" private const val DATABASES_PATH = "databases"
private const val QUEUE_PATH = "queue"
private const val SETTINGS_PATH = "prefs" private const val SETTINGS_PATH = "prefs"
private const val IMAGES_PATH = "userImages" private const val IMAGES_PATH = "userImages"
private const val CUSTOM_ARTISTS_PATH = "artistImages" private const val CUSTOM_ARTISTS_PATH = "artistImages"
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]" private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
private const val queueDatabase = "music_playback_state.db"
private fun ZipEntry.isDatabaseEntry(): Boolean { private fun ZipEntry.isDatabaseEntry(): Boolean {
return name.startsWith(DATABASES_PATH) return name.startsWith(DATABASES_PATH)
@ -243,6 +294,10 @@ object BackupHelper {
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs") return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
} }
private fun ZipEntry.isQueueEntry(): Boolean {
return name.startsWith(QUEUE_PATH)
}
private fun ZipEntry.getFileName(): String { private fun ZipEntry.getFileName(): String {
return name.substring(name.lastIndexOf(File.separator)) return name.substring(name.lastIndexOf(File.separator))
} }
@ -261,4 +316,12 @@ fun CharSequence.sanitize(): String {
.replace("|", "_") .replace("|", "_")
.replace("\\", "_") .replace("\\", "_")
.replace("&", "_") .replace("&", "_")
}
enum class BackupContent {
SETTINGS,
USER_IMAGES,
CUSTOM_ARTIST_IMAGES,
PLAYLISTS,
QUEUE
} }

View file

@ -31,15 +31,15 @@ object M3UWriter : M3UConstants {
val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION) val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION)
val songs = playlist.getSongs() val songs = playlist.getSongs()
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
val bw = BufferedWriter(FileWriter(file)) BufferedWriter(FileWriter(file)).use { bw ->
bw.write(M3UConstants.HEADER) bw.write(M3UConstants.HEADER)
for (song in songs) { for (song in songs) {
bw.newLine() bw.newLine()
bw.write(M3UConstants.ENTRY + song.duration + M3UConstants.DURATION_SEPARATOR + song.artistName + " - " + song.title) bw.write(M3UConstants.ENTRY + song.duration + M3UConstants.DURATION_SEPARATOR + song.artistName + " - " + song.title)
bw.newLine() bw.newLine()
bw.write(song.data) bw.write(song.data)
}
} }
bw.close()
} }
return file return file
} }
@ -54,15 +54,15 @@ object M3UWriter : M3UConstants {
it.songPrimaryKey it.songPrimaryKey
}.toSongs() }.toSongs()
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
val bufferedWriter = BufferedWriter(FileWriter(file)) BufferedWriter(FileWriter(file)).use { bw->
bufferedWriter.write(M3UConstants.HEADER) bw.write(M3UConstants.HEADER)
songs.forEach { songs.forEach {
bufferedWriter.newLine() bw.newLine()
bufferedWriter.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title) bw.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title)
bufferedWriter.newLine() bw.newLine()
bufferedWriter.write(it.data) bw.write(it.data)
}
} }
bufferedWriter.close()
} }
return file return file
} }
@ -72,15 +72,15 @@ object M3UWriter : M3UConstants {
it.songPrimaryKey it.songPrimaryKey
}.toSongs() }.toSongs()
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
val bufferedWriter = outputStream.bufferedWriter() outputStream.bufferedWriter().use{ bw->
bufferedWriter.write(M3UConstants.HEADER) bw.write(M3UConstants.HEADER)
songs.forEach { songs.forEach {
bufferedWriter.newLine() bw.newLine()
bufferedWriter.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title) bw.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title)
bufferedWriter.newLine() bw.newLine()
bufferedWriter.write(it.data) bw.write(it.data)
}
} }
bufferedWriter.close()
} }
outputStream.flush() outputStream.flush()
outputStream.close() outputStream.close()

View file

@ -0,0 +1,711 @@
/*
* Copyright (C) 2017 wangchenyan
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package code.name.monkey.retromusic.lyrics
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.os.AsyncTask
import android.os.Looper
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.text.TextUtils
import android.text.format.DateUtils
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.Scroller
import androidx.core.content.ContextCompat
import code.name.monkey.retromusic.R
import java.io.File
import java.util.*
import kotlin.math.abs
/**
* 歌词 Created by wcy on 2015/11/9.
*/
@SuppressLint("StaticFieldLeak")
class CoverLrcView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val mLrcEntryList: MutableList<LrcEntry> = ArrayList()
private val mLrcPaint = TextPaint()
private val mTimePaint = TextPaint()
private var mTimeFontMetrics: Paint.FontMetrics? = null
private var mPlayDrawable: Drawable? = null
private var mDividerHeight = 0f
private var mAnimationDuration: Long = 0
private var mNormalTextColor = 0
private var mNormalTextSize = 0f
private var mCurrentTextColor = 0
private var mCurrentTextSize = 0f
private var mTimelineTextColor = 0
private var mTimelineColor = 0
private var mTimeTextColor = 0
private var mDrawableWidth = 0
private var mTimeTextWidth = 0
private var mDefaultLabel: String? = null
private var mLrcPadding = 0f
private var mOnPlayClickListener: OnPlayClickListener? = null
private var mAnimator: ValueAnimator? = null
private var mGestureDetector: GestureDetector? = null
private var mScroller: Scroller? = null
private var mOffset = 0f
private var mCurrentLine = 0
private var flag: Any? = null
private var isShowTimeline = false
private var isTouching = false
private var isFling = false
private var mTextGravity // 歌词显示位置,靠左/居中/靠右
= 0
private val hideTimelineRunnable = Runnable {
if (hasLrc() && isShowTimeline) {
isShowTimeline = false
smoothScrollTo(mCurrentLine)
}
}
/**
* 手势监听器
*/
private val mSimpleOnGestureListener: SimpleOnGestureListener =
object : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
if (mOffset != getOffset(0)) {
parent.requestDisallowInterceptTouchEvent(true)
}
if (hasLrc() && mOnPlayClickListener != null) {
mScroller!!.forceFinished(true)
removeCallbacks(hideTimelineRunnable)
isTouching = true
isShowTimeline = true
invalidate()
return true
}
return super.onDown(e)
}
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
if (mOffset == getOffset(0) && distanceY < 0F) {
return super.onScroll(e1, e2, distanceX, distanceY)
}
if (hasLrc()) {
mOffset += -distanceY
mOffset = mOffset.coerceAtMost(getOffset(0))
mOffset = mOffset.coerceAtLeast(getOffset(mLrcEntryList.size - 1))
invalidate()
parent.requestDisallowInterceptTouchEvent(true)
return true
}
return super.onScroll(e1, e2, distanceX, distanceY)
}
override fun onFling(
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
if (hasLrc()) {
mScroller!!.fling(
0,
mOffset.toInt(),
0,
velocityY.toInt(),
0,
0,
getOffset(mLrcEntryList.size - 1).toInt(),
getOffset(0).toInt()
)
isFling = true
return true
}
return super.onFling(e1, e2, velocityX, velocityY)
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
if (hasLrc()
&& isShowTimeline
&& mPlayDrawable!!.bounds.contains(e.x.toInt(), e.y.toInt())
) {
val centerLine = centerLine
val centerLineTime = mLrcEntryList[centerLine].time
// onPlayClick 消费了才更新 UI
if (mOnPlayClickListener != null && mOnPlayClickListener!!.onPlayClick(
centerLineTime
)
) {
isShowTimeline = false
removeCallbacks(hideTimelineRunnable)
mCurrentLine = centerLine
invalidate()
return true
}
}
return super.onSingleTapConfirmed(e)
}
}
private fun init(attrs: AttributeSet?) {
val ta = context.obtainStyledAttributes(attrs, R.styleable.LrcView)
mCurrentTextSize = ta.getDimension(
R.styleable.LrcView_lrcTextSize, resources.getDimension(R.dimen.lrc_text_size)
)
mNormalTextSize = ta.getDimension(
R.styleable.LrcView_lrcNormalTextSize,
resources.getDimension(R.dimen.lrc_text_size)
)
if (mNormalTextSize == 0f) {
mNormalTextSize = mCurrentTextSize
}
mDividerHeight = ta.getDimension(
R.styleable.LrcView_lrcDividerHeight,
resources.getDimension(R.dimen.lrc_divider_height)
)
val defDuration = resources.getInteger(R.integer.lrc_animation_duration)
mAnimationDuration =
ta.getInt(R.styleable.LrcView_lrcAnimationDuration, defDuration).toLong()
mAnimationDuration =
if (mAnimationDuration < 0) defDuration.toLong() else mAnimationDuration
mNormalTextColor = ta.getColor(
R.styleable.LrcView_lrcNormalTextColor,
ContextCompat.getColor(context, R.color.lrc_normal_text_color)
)
mCurrentTextColor = ta.getColor(
R.styleable.LrcView_lrcCurrentTextColor,
ContextCompat.getColor(context, R.color.lrc_current_text_color)
)
mTimelineTextColor = ta.getColor(
R.styleable.LrcView_lrcTimelineTextColor,
ContextCompat.getColor(context, R.color.lrc_timeline_text_color)
)
mDefaultLabel = ta.getString(R.styleable.LrcView_lrcLabel)
mDefaultLabel =
if (TextUtils.isEmpty(mDefaultLabel)) context.getString(R.string.empty) else mDefaultLabel
mLrcPadding = ta.getDimension(R.styleable.LrcView_lrcPadding, 0f)
mTimelineColor = ta.getColor(
R.styleable.LrcView_lrcTimelineColor,
ContextCompat.getColor(context, R.color.lrc_timeline_color)
)
val timelineHeight = ta.getDimension(
R.styleable.LrcView_lrcTimelineHeight,
resources.getDimension(R.dimen.lrc_timeline_height)
)
mPlayDrawable = ta.getDrawable(R.styleable.LrcView_lrcPlayDrawable)
mPlayDrawable =
if (mPlayDrawable == null) ContextCompat.getDrawable(
context,
R.drawable.ic_play_arrow
) else mPlayDrawable
mTimeTextColor = ta.getColor(
R.styleable.LrcView_lrcTimeTextColor,
ContextCompat.getColor(context, R.color.lrc_time_text_color)
)
val timeTextSize = ta.getDimension(
R.styleable.LrcView_lrcTimeTextSize,
resources.getDimension(R.dimen.lrc_time_text_size)
)
mTextGravity = ta.getInteger(R.styleable.LrcView_lrcTextGravity, LrcEntry.GRAVITY_CENTER)
ta.recycle()
mDrawableWidth = resources.getDimension(R.dimen.lrc_drawable_width).toInt()
mTimeTextWidth = resources.getDimension(R.dimen.lrc_time_width).toInt()
mLrcPaint.isAntiAlias = true
mLrcPaint.textSize = mCurrentTextSize
mLrcPaint.textAlign = Paint.Align.LEFT
mTimePaint.isAntiAlias = true
mTimePaint.textSize = timeTextSize
mTimePaint.textAlign = Paint.Align.CENTER
mTimePaint.strokeWidth = timelineHeight
mTimePaint.strokeCap = Paint.Cap.ROUND
mTimeFontMetrics = mTimePaint.fontMetrics
mGestureDetector = GestureDetector(context, mSimpleOnGestureListener)
mGestureDetector!!.setIsLongpressEnabled(false)
mScroller = Scroller(context)
}
/** 设置非当前行歌词字体颜色 */
fun setNormalColor(normalColor: Int) {
mNormalTextColor = normalColor
postInvalidate()
}
/** 普通歌词文本字体大小 */
fun setNormalTextSize(size: Float) {
mNormalTextSize = size
}
/** 当前歌词文本字体大小 */
fun setCurrentTextSize(size: Float) {
mCurrentTextSize = size
}
/** 设置当前行歌词的字体颜色 */
fun setCurrentColor(currentColor: Int) {
mCurrentTextColor = currentColor
postInvalidate()
}
/** 设置拖动歌词时选中歌词的字体颜色 */
fun setTimelineTextColor(timelineTextColor: Int) {
mTimelineTextColor = timelineTextColor
postInvalidate()
}
/** 设置拖动歌词时时间线的颜色 */
fun setTimelineColor(timelineColor: Int) {
mTimelineColor = timelineColor
postInvalidate()
}
/** 设置拖动歌词时右侧时间字体颜色 */
fun setTimeTextColor(timeTextColor: Int) {
mTimeTextColor = timeTextColor
postInvalidate()
}
/**
* 设置歌词是否允许拖动
*
* @param draggable 是否允许拖动
* @param onPlayClickListener 设置歌词拖动后播放按钮点击监听器如果允许拖动则不能为 null
*/
fun setDraggable(draggable: Boolean, onPlayClickListener: OnPlayClickListener?) {
mOnPlayClickListener = if (draggable) {
requireNotNull(onPlayClickListener) { "if draggable == true, onPlayClickListener must not be null" }
onPlayClickListener
} else {
null
}
}
/**
* 设置播放按钮点击监听器
*
* @param onPlayClickListener 如果为非 null 则激活歌词拖动功能否则将将禁用歌词拖动功能
*/
@Deprecated("use {@link #setDraggable(boolean, OnPlayClickListener)} instead")
fun setOnPlayClickListener(onPlayClickListener: OnPlayClickListener?) {
mOnPlayClickListener = onPlayClickListener
}
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
fun setLabel(label: String?) {
runOnUi {
mDefaultLabel = label
invalidate()
}
}
/**
* 加载歌词文件
*
* @param lrcFile 歌词文件
*/
fun loadLrc(lrcFile: File) {
loadLrc(lrcFile, null)
}
/**
* 加载双语歌词文件两种语言的歌词时间戳需要一致
*
* @param mainLrcFile 第一种语言歌词文件
* @param secondLrcFile 第二种语言歌词文件
*/
fun loadLrc(mainLrcFile: File, secondLrcFile: File?) {
runOnUi {
reset()
val sb = StringBuilder("file://")
sb.append(mainLrcFile.path)
if (secondLrcFile != null) {
sb.append("#").append(secondLrcFile.path)
}
val flag = sb.toString()
this.flag = flag
object : AsyncTask<File?, Int?, List<LrcEntry>>() {
override fun doInBackground(vararg params: File?): List<LrcEntry>? {
return LrcUtils.parseLrc(params)
}
override fun onPostExecute(lrcEntries: List<LrcEntry>) {
if (flag === flag) {
onLrcLoaded(lrcEntries)
this@CoverLrcView.flag = null
}
}
}.execute(mainLrcFile, secondLrcFile)
}
}
/**
* 加载歌词文本
*
* @param lrcText 歌词文本
*/
fun loadLrc(lrcText: String?) {
loadLrc(lrcText, null)
}
/**
* 加载双语歌词文本两种语言的歌词时间戳需要一致
*
* @param mainLrcText 第一种语言歌词文本
* @param secondLrcText 第二种语言歌词文本
*/
fun loadLrc(mainLrcText: String?, secondLrcText: String?) {
runOnUi {
reset()
val sb = StringBuilder("file://")
sb.append(mainLrcText)
if (secondLrcText != null) {
sb.append("#").append(secondLrcText)
}
val flag = sb.toString()
this.flag = flag
object : AsyncTask<String?, Int?, List<LrcEntry>>() {
override fun doInBackground(vararg params: String?): List<LrcEntry>? {
return LrcUtils.parseLrc(params)
}
override fun onPostExecute(lrcEntries: List<LrcEntry>) {
if (flag === flag) {
onLrcLoaded(lrcEntries)
this@CoverLrcView.flag = null
}
}
}.execute(mainLrcText, secondLrcText)
}
}
/**
* 加载在线歌词
*
* @param lrcUrl 歌词文件的网络地址
* @param charset 编码格式
*/
/**
* 加载在线歌词默认使用 utf-8 编码
*
* @param lrcUrl 歌词文件的网络地址
*/
@JvmOverloads
fun loadLrcByUrl(lrcUrl: String, charset: String? = "utf-8") {
val flag = "url://$lrcUrl"
this.flag = flag
object : AsyncTask<String?, Int?, String>() {
override fun doInBackground(vararg params: String?): String? {
return LrcUtils.getContentFromNetwork(params[0], params[1])
}
override fun onPostExecute(lrcText: String) {
if (flag === flag) {
loadLrc(lrcText)
}
}
}.execute(lrcUrl, charset)
}
/**
* 歌词是否有效
*
* @return true如果歌词有效否则false
*/
fun hasLrc(): Boolean {
return mLrcEntryList.isNotEmpty()
}
/**
* 刷新歌词
*
* @param time 当前播放时间
*/
fun updateTime(time: Long) {
runOnUi {
if (!hasLrc()) {
return@runOnUi
}
val line = findShowLine(time)
if (line != mCurrentLine) {
mCurrentLine = line
if (!isShowTimeline) {
smoothScrollTo(line)
} else {
invalidate()
}
}
}
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (changed) {
initPlayDrawable()
initEntryList()
if (hasLrc()) {
smoothScrollTo(mCurrentLine, 0L)
}
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val centerY = height / 2
// 无歌词文件
if (!hasLrc()) {
mLrcPaint.color = mCurrentTextColor
@SuppressLint("DrawAllocation") val staticLayout = StaticLayout(
mDefaultLabel,
mLrcPaint,
lrcWidth.toInt(),
Layout.Alignment.ALIGN_CENTER,
1f,
0f,
false
)
drawText(canvas, staticLayout, centerY.toFloat())
return
}
val centerLine = centerLine
if (isShowTimeline) {
mPlayDrawable?.draw(canvas)
mTimePaint.color = mTimeTextColor
val timeText = LrcUtils.formatTime(mLrcEntryList[centerLine].time)
val timeX = (width - mTimeTextWidth / 2).toFloat()
val timeY = centerY - (mTimeFontMetrics!!.descent + mTimeFontMetrics!!.ascent) / 2
canvas.drawText(timeText, timeX, timeY, mTimePaint)
}
canvas.translate(0f, mOffset)
var y = 0f
for (i in mLrcEntryList.indices) {
if (i > 0) {
y += ((mLrcEntryList[i - 1].height + mLrcEntryList[i].height shr 1)
+ mDividerHeight)
}
if (i == mCurrentLine) {
mLrcPaint.textSize = mCurrentTextSize
mLrcPaint.color = mCurrentTextColor
} else if (isShowTimeline && i == centerLine) {
mLrcPaint.color = mTimelineTextColor
} else {
mLrcPaint.textSize = mNormalTextSize
mLrcPaint.color = mNormalTextColor
}
drawText(canvas, mLrcEntryList[i].staticLayout, y)
}
}
/**
* 画一行歌词
*
* @param y 歌词中心 Y 坐标
*/
private fun drawText(canvas: Canvas, staticLayout: StaticLayout, y: Float) {
canvas.save()
canvas.translate(mLrcPadding, y - (staticLayout.height shr 1))
staticLayout.draw(canvas)
canvas.restore()
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_UP
|| event.action == MotionEvent.ACTION_CANCEL
) {
isTouching = false
if (hasLrc() && !isFling) {
adjustCenter()
postDelayed(hideTimelineRunnable, TIMELINE_KEEP_TIME)
}
}
return mGestureDetector!!.onTouchEvent(event)
}
override fun computeScroll() {
if (mScroller!!.computeScrollOffset()) {
mOffset = mScroller!!.currY.toFloat()
invalidate()
}
if (isFling && mScroller!!.isFinished) {
isFling = false
if (hasLrc() && !isTouching) {
adjustCenter()
postDelayed(hideTimelineRunnable, TIMELINE_KEEP_TIME)
}
}
}
override fun onDetachedFromWindow() {
removeCallbacks(hideTimelineRunnable)
super.onDetachedFromWindow()
}
private fun onLrcLoaded(entryList: List<LrcEntry>?) {
if (entryList != null && entryList.isNotEmpty()) {
mLrcEntryList.addAll(entryList)
}
mLrcEntryList.sort()
initEntryList()
invalidate()
}
private fun initPlayDrawable() {
val l = (mTimeTextWidth - mDrawableWidth) / 2
val t = height / 2 - mDrawableWidth / 2
val r = l + mDrawableWidth
val b = t + mDrawableWidth
mPlayDrawable!!.setBounds(l, t, r, b)
}
private fun initEntryList() {
if (!hasLrc() || width == 0) {
return
}
for (lrcEntry in mLrcEntryList) {
lrcEntry.init(mLrcPaint, lrcWidth.toInt(), mTextGravity)
}
mOffset = (height / 2).toFloat()
}
fun reset() {
endAnimation()
mScroller!!.forceFinished(true)
isShowTimeline = false
isTouching = false
isFling = false
removeCallbacks(hideTimelineRunnable)
mLrcEntryList.clear()
mOffset = 0f
mCurrentLine = 0
invalidate()
}
/** 将中心行微调至正中心 */
private fun adjustCenter() {
smoothScrollTo(centerLine, ADJUST_DURATION)
}
/** 滚动到某一行 */
/** 滚动到某一行 */
private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) {
val offset = getOffset(line)
endAnimation()
mAnimator = ValueAnimator.ofFloat(mOffset, offset).apply {
this.duration = duration
interpolator = LinearInterpolator()
addUpdateListener { animation: ValueAnimator ->
mOffset = animation.animatedValue as Float
invalidate()
}
LrcUtils.resetDurationScale()
start()
}
}
/** 结束滚动动画 */
private fun endAnimation() {
if (mAnimator != null && mAnimator!!.isRunning) {
mAnimator!!.end()
}
}
/** 二分法查找当前时间应该显示的行数(最后一个 <= time 的行数) */
private fun findShowLine(time: Long): Int {
var left = 0
var right = mLrcEntryList.size
while (left <= right) {
val middle = (left + right) / 2
val middleTime = mLrcEntryList[middle].time
if (time < middleTime) {
right = middle - 1
} else {
if (middle + 1 >= mLrcEntryList.size || time < mLrcEntryList[middle + 1].time) {
return middle
}
left = middle + 1
}
}
return 0
}
/** 获取当前在视图中央的行数 */
private val centerLine: Int
get() {
var centerLine = 0
var minDistance = Float.MAX_VALUE
for (i in mLrcEntryList.indices) {
if (abs(mOffset - getOffset(i)) < minDistance) {
minDistance = abs(mOffset - getOffset(i))
centerLine = i
}
}
return centerLine
}
/** 获取歌词距离视图顶部的距离 采用懒加载方式 */
private fun getOffset(line: Int): Float {
if (mLrcEntryList[line].offset == Float.MIN_VALUE) {
var offset = (height / 2).toFloat()
for (i in 1..line) {
offset -= ((mLrcEntryList[i - 1].height + mLrcEntryList[i].height shr 1)
+ mDividerHeight)
}
mLrcEntryList[line].offset = offset
}
return mLrcEntryList[line].offset
}
/** 获取歌词宽度 */
private val lrcWidth: Float
get() = width - mLrcPadding * 2
/** 在主线程中运行 */
private fun runOnUi(r: Runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
r.run()
} else {
post(r)
}
}
/** 播放按钮点击监听器,点击后应该跳转到指定播放位置 */
interface OnPlayClickListener {
/**
* 播放按钮被点击应该跳转到指定播放位置
*
* @return 是否成功消费该事件如果成功消费则会更新UI
*/
fun onPlayClick(time: Long): Boolean
}
companion object {
private const val ADJUST_DURATION: Long = 100
private const val TIMELINE_KEEP_TIME = 4 * DateUtils.SECOND_IN_MILLIS
}
init {
init(attrs)
}
}

View file

@ -30,8 +30,6 @@ import androidx.media.app.NotificationCompat.MediaStyle
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper

View file

@ -15,12 +15,7 @@
package code.name.monkey.retromusic.util package code.name.monkey.retromusic.util
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri
import code.name.monkey.retromusic.R
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.play.core.review.ReviewManagerFactory import com.google.android.play.core.review.ReviewManagerFactory
object AppRater { object AppRater {

View file

@ -14,7 +14,6 @@
package code.name.monkey.retromusic.util package code.name.monkey.retromusic.util
import android.content.ContentValues
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@ -32,14 +31,6 @@ class RingtoneManager(val context: Context) {
fun setRingtone(song: Song) { fun setRingtone(song: Song) {
val resolver = context.contentResolver val resolver = context.contentResolver
val uri = getSongFileUri(song.id) val uri = getSongFileUri(song.id)
try {
val values = ContentValues(2)
values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, "1")
values.put(MediaStore.Audio.AudioColumns.IS_ALARM, "1")
resolver.update(uri, values, null, null)
} catch (ignored: UnsupportedOperationException) {
return
}
try { try {
val cursor = resolver.query( val cursor = resolver.query(

View file

@ -26,7 +26,6 @@ import androidx.core.graphics.BlendModeCompat.SRC_IN
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import com.google.android.material.progressindicator.CircularProgressIndicator
object ViewUtil { object ViewUtil {

View file

@ -29,9 +29,14 @@ object ThemeManager {
context: Context context: Context
): Int = when (context.generalThemeValue) { ): Int = when (context.generalThemeValue) {
LIGHT -> AppCompatDelegate.MODE_NIGHT_NO LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
DARK, DARK -> AppCompatDelegate.MODE_NIGHT_YES
BLACK -> AppCompatDelegate.MODE_NIGHT_YES BLACK -> {
AUTO -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM if (PreferenceUtil.baseTheme == "dark") {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
}
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
} }
} }

View file

@ -16,13 +16,11 @@ package code.name.monkey.retromusic.views
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.drawable.ColorDrawable
import android.util.AttributeSet import android.util.AttributeSet
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.NavigationViewUtil import code.name.monkey.appthemehelper.util.NavigationViewUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
@ -49,7 +47,6 @@ class BottomNavigationBarTinted @JvmOverloads constructor(
accentColor accentColor
) )
itemRippleColor = ColorStateList.valueOf(accentColor.addAlpha(0.08F)) itemRippleColor = ColorStateList.valueOf(accentColor.addAlpha(0.08F))
background = ColorDrawable(ATHUtil.resolveColor(context, R.attr.bottomSheetTint))
itemActiveIndicatorColor = ColorStateList.valueOf(accentColor.addAlpha(0.12F)) itemActiveIndicatorColor = ColorStateList.valueOf(accentColor.addAlpha(0.12F))
} }
} }

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@android:anim/fade_in"
android:animationOrder="normal"
android:delay="15%" />

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?colorSurface"/>
<corners android:radius="28dp" />
</shape>

View file

@ -3,7 +3,8 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface"
android:fitsSystemWindows="true">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -16,20 +17,22 @@
app:navigationIcon="@drawable/ic_keyboard_backspace_black" app:navigationIcon="@drawable/ic_keyboard_backspace_black"
app:title="@string/action_tag_editor" /> app:title="@string/action_tag_editor" />
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/imageContainer" android:id="@+id/imageContainer"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="16dp" android:layout_margin="16dp"
android:layout_weight="1"
android:transitionName="@string/transition_album_art" android:transitionName="@string/transition_album_art"
app:cardCornerRadius="24dp" app:cardCornerRadius="24dp"
app:cardElevation="8dp"> app:cardElevation="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/editorImage" android:id="@+id/editorImage"
@ -50,10 +53,14 @@
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/content" android:id="@+id/content"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageContainer"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -137,7 +144,7 @@
android:layout_height="52dp" /> android:layout_height="52dp" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton

View file

@ -0,0 +1,321 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
android:orientation="vertical">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black"
app:title="@string/action_tag_editor" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.card.MaterialCardView
android:id="@+id/imageContainer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="40dp"
android:transitionName="@string/transition_album_art"
app:cardCornerRadius="@dimen/m3_card_large_radius"
app:cardElevation="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/editorImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:srcCompat="@tools:sample/backgrounds/scenic[5]" />
</com.google.android.material.card.MaterialCardView>
<androidx.core.widget.NestedScrollView
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="0dp"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageContainer"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/songTextContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/songText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/song"
android:inputType="text|textCapWords"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/albumTextContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/albumText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/album"
android:inputType="text|textCapWords"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/artistContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/artistText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/artist"
android:inputType="text|textCapWords"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/albumArtistContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/albumArtistText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/album_artist"
android:inputType="text|textCapWords"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/composerContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/songComposerText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/composer"
android:inputType="text|textCapWords"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/genreContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_weight="1"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/genreText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/genre"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/yearContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/yearText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/year"
android:inputType="text|number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/trackNumberContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_weight="1"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/trackNumberText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/track_hint"
android:inputType="text|number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/discNumberContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/discNumberText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/disc_hint"
android:inputType="text|number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/lyricsContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/lyricsText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/lyrics"
android:inputType="textMultiLine"
android:maxLines="15" />
</com.google.android.material.textfield.TextInputLayout>
<Space
android:layout_width="match_parent"
android:layout_height="52dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/saveTags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:gravity="center"
android:paddingStart="32dp"
android:paddingTop="12dp"
android:paddingEnd="32dp"
android:paddingBottom="12dp"
android:text="@string/save"
app:cornerRadius="25dp"
app:icon="@drawable/ic_save"
app:iconGravity="textStart"
tools:backgroundTint="@color/md_red_400" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -55,6 +55,7 @@
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:fillViewport="true" android:fillViewport="true"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"

View file

@ -55,6 +55,7 @@
android:layout_weight="1" android:layout_weight="1"
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"

View file

@ -63,6 +63,7 @@
android:layout_marginEnd="@dimen/toolbar_margin_horizontal" android:layout_marginEnd="@dimen/toolbar_margin_horizontal"
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
android:transitionGroup="true" android:transitionGroup="true"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">

View file

@ -54,29 +54,46 @@
app:navigationIcon="@drawable/ic_keyboard_arrow_down_black" app:navigationIcon="@drawable/ic_keyboard_arrow_down_black"
tools:layout_editor_absoluteY="24dp" /> tools:layout_editor_absoluteY="24dp" />
<FrameLayout
<code.name.monkey.retromusic.views.SeekArc
android:id="@+id/volumeSeekBar"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginStart="16dp" android:padding="10dp"
android:layout_marginTop="16dp" app:layout_constraintBottom_toBottomOf="@id/volumeSeekBar"
android:layout_marginEnd="16dp" app:layout_constraintEnd_toEndOf="@id/volumeSeekBar"
android:layout_marginBottom="16dp" app:layout_constraintStart_toStartOf="@id/volumeSeekBar"
android:padding="24dp" app:layout_constraintTop_toTopOf="@id/volumeSeekBar">
app:arcColor="?android:attr/colorControlHighlight"
app:arcWidth="4dp" <code.name.monkey.retromusic.views.RetroShapeableImageView
android:id="@+id/album_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:srcCompat="@tools:sample/backgrounds/scenic[6]" />
<code.name.monkey.retromusic.views.RetroShapeableImageView
android:id="@+id/album_cover_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.5"
android:scaleType="centerCrop" />
</FrameLayout>
<me.tankery.lib.circularseekbar.CircularSeekBar
android:id="@+id/volumeSeekBar"
android:layout_width="wrap_content"
android:layout_height="300dp"
app:cs_circle_stroke_width="4dp"
app:cs_end_angle="60"
app:cs_move_outside_circle="true"
app:cs_pointer_color="@color/md_grey_200"
app:cs_pointer_halo_width="0dp"
app:cs_pointer_stroke_width="20dp"
app:cs_start_angle="120"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="@id/guideline" app:layout_constraintEnd_toEndOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/playerToolbar" app:layout_constraintTop_toBottomOf="@id/playerToolbar"
app:progressWidth="4dp"
app:rotation="180"
app:roundEdges="true"
app:startAngle="30"
app:sweepAngle="300"
app:touchInside="false"
tools:progress="50" /> tools:progress="50" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton

View file

@ -61,6 +61,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
android:transitionGroup="true" android:transitionGroup="true"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">

View file

@ -42,6 +42,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="@integer/overScrollMode"
android:paddingBottom="96dp" android:paddingBottom="96dp"
android:scrollbars="none" android:scrollbars="none"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"

View file

@ -43,7 +43,6 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:text="@string/my_top_tracks" android:text="@string/my_top_tracks"
app:icon="@drawable/ic_trending_up" app:icon="@drawable/ic_trending_up"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/actionShuffle" app:layout_constraintEnd_toStartOf="@+id/actionShuffle"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/history" app:layout_constraintStart_toStartOf="@+id/history"

View file

@ -26,7 +26,8 @@
android:id="@+id/content" android:id="@+id/content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
android:overScrollMode="@integer/overScrollMode">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -24,6 +24,7 @@
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout <LinearLayout

View file

@ -28,6 +28,7 @@
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout <LinearLayout

View file

@ -6,23 +6,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<code.name.monkey.retromusic.views.StatusBarView
android:id="@+id/status_bar"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="?attr/colorSurface"
tools:ignore="UnusedAttribute" />
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorPrimary" android:fitsSystemWindows="true"
app:liftOnScroll="true"> app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
@ -37,15 +31,18 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<code.name.monkey.retromusic.views.LollipopFixedWebView <code.name.monkey.retromusic.views.LollipopFixedWebView
android:id="@+id/license" android:id="@+id/license"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="wrap_content"
android:scrollbars="none"
android:fitsSystemWindows="true"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,8 +1,84 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
tools:context=".fragments.backup.RestoreActivity"> android:orientation="vertical"
android:padding="16dp">
</androidx.constraintlayout.widget.ConstraintLayout> <com.google.android.material.textfield.TextInputLayout
android:id="@+id/backupNameContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:hint="@string/label_file_name">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/backupName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/materialTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/choose_restore_title" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/check_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:minHeight="48dp"
android:text="@string/action_settings" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/check_databases"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:text="@string/databases_description" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/check_queue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:text="@string/now_playing_queue" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/check_user_images"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:text="@string/user_images_description" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/check_artist_images"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:text="@string/custom_artist_images" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end">
<com.google.android.material.button.MaterialButton
android:id="@+id/cancel_button"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/action_cancel" />
<com.google.android.material.button.MaterialButton
android:id="@+id/restore_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/restore" />
</LinearLayout>
</LinearLayout>

View file

@ -1,5 +1,6 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
@ -24,6 +25,7 @@
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout <LinearLayout
@ -32,6 +34,28 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<code.name.monkey.retromusic.views.WidthFitSquareCardView
android:id="@+id/imageContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:transitionName="@string/transition_album_art"
app:cardCornerRadius="@dimen/m3_card_large_radius"
app:cardElevation="8dp"
app:cardUseCompatPadding="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/editorImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:srcCompat="@tools:sample/backgrounds/scenic[5]" />
</code.name.monkey.retromusic.views.WidthFitSquareCardView>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/songTextContainer" android:id="@+id/songTextContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
@ -142,7 +166,6 @@
android:layout_weight="1" android:layout_weight="1"
app:hintEnabled="true"> app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/genreText" android:id="@+id/genreText"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -165,7 +188,6 @@
android:layout_weight="1" android:layout_weight="1"
app:hintEnabled="true"> app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/yearText" android:id="@+id/yearText"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -179,26 +201,56 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>
<com.google.android.material.textfield.TextInputLayout <LinearLayout
android:id="@+id/trackNumberContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:baselineAligned="false"
app:hintEnabled="true"> android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
<com.google.android.material.textfield.TextInputEditText android:id="@+id/trackNumberContainer"
android:id="@+id/trackNumberText" style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_marginTop="8dp"
android:gravity="center_vertical" android:layout_weight="1"
android:hint="@string/track_hint" app:hintEnabled="true">
android:inputType="text|number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/trackNumberText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/track_hint"
android:inputType="text|number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/discNumberContainer"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
app:hintEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/discNumberText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:hint="@string/disc_hint"
android:inputType="text|number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/lyricsContainer" android:id="@+id/lyricsContainer"

View file

@ -4,6 +4,7 @@
android:id="@+id/container" android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<include <include

View file

@ -59,6 +59,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" android:nestedScrollingEnabled="false"
android:overScrollMode="@integer/overScrollMode"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/songTitle" app:layout_constraintTop_toBottomOf="@id/songTitle"

View file

@ -33,6 +33,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout

View file

@ -60,6 +60,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" android:nestedScrollingEnabled="false"
android:overScrollMode="@integer/overScrollMode"
app:barrierDirection="bottom" app:barrierDirection="bottom"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View file

@ -32,6 +32,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout

View file

@ -61,6 +61,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<code.name.monkey.retromusic.views.insets.InsetsConstraintLayout <code.name.monkey.retromusic.views.insets.InsetsConstraintLayout

View file

@ -17,15 +17,11 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?colorSurface"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:orientation="vertical"> android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface" />
<FrameLayout <FrameLayout
android:id="@+id/statusBarContainer" android:id="@+id/statusBarContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -85,23 +81,45 @@
app:layout_constraintTop_toBottomOf="@+id/titleContainer" app:layout_constraintTop_toBottomOf="@+id/titleContainer"
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/lorem/random" />
<code.name.monkey.retromusic.views.SeekArc <FrameLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="10dp"
app:layout_constraintBottom_toBottomOf="@id/volumeSeekBar"
app:layout_constraintEnd_toEndOf="@id/volumeSeekBar"
app:layout_constraintStart_toStartOf="@id/volumeSeekBar"
app:layout_constraintTop_toTopOf="@id/volumeSeekBar">
<code.name.monkey.retromusic.views.RetroShapeableImageView
android:id="@+id/album_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:srcCompat="@tools:sample/backgrounds/scenic[6]" />
<code.name.monkey.retromusic.views.RetroShapeableImageView
android:id="@+id/album_cover_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.5"
android:scaleType="centerCrop" />
</FrameLayout>
<me.tankery.lib.circularseekbar.CircularSeekBar
android:id="@+id/volumeSeekBar" android:id="@+id/volumeSeekBar"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="300dp" android:layout_height="300dp"
android:padding="28dp" app:cs_circle_stroke_width="4dp"
app:arcColor="?android:attr/colorControlHighlight" app:cs_end_angle="60"
app:arcWidth="4dp" app:cs_move_outside_circle="true"
app:cs_pointer_color="@color/md_grey_200"
app:cs_pointer_halo_width="0dp"
app:cs_pointer_stroke_width="20dp"
app:cs_start_angle="120"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:progressWidth="4dp"
app:rotation="180"
app:roundEdges="true"
app:startAngle="30"
app:sweepAngle="300"
app:touchInside="false"
tools:progress="50" /> tools:progress="50" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
@ -139,14 +157,15 @@
app:tint="@color/md_green_500" /> app:tint="@color/md_green_500" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/materialTextView3"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/volume" android:text="@string/volume"
android:textAppearance="@style/TextViewSubtitle2" android:textAppearance="@style/TextViewSubtitle2"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:layout_constraintBottom_toBottomOf="@+id/volumeSeekBar"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/volumeSeekBar" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/volumeSeekBar" />
<androidx.appcompat.widget.AppCompatSeekBar <androidx.appcompat.widget.AppCompatSeekBar

View file

@ -140,7 +140,8 @@
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:listitem="@layout/item_list" /> android:overScrollMode="@integer/overScrollMode"
tools:listitem="@layout/item_list" />
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>

View file

@ -153,7 +153,6 @@
android:layout_height="52dp" android:layout_height="52dp"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:background="?colorAccent" android:background="?colorAccent"
android:foreground="?attr/rectSelector"
android:padding="12dp" android:padding="12dp"
android:scaleType="fitCenter" android:scaleType="fitCenter"
tools:ignore="MissingPrefix" tools:ignore="MissingPrefix"

View file

@ -82,6 +82,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="@integer/overScrollMode"
android:scrollbars="none" android:scrollbars="none"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -150,6 +150,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
android:overScrollMode="@integer/overScrollMode"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View file

@ -61,6 +61,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants" android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:overScrollMode="@integer/overScrollMode"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<code.name.monkey.retromusic.views.insets.InsetsConstraintLayout <code.name.monkey.retromusic.views.insets.InsetsConstraintLayout

View file

@ -10,7 +10,7 @@
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/songCurrentProgress" android:id="@+id/songCurrentProgress"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:gravity="center" android:gravity="center"
@ -41,7 +41,7 @@
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/songTotalTime" android:id="@+id/songTotalTime"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:gravity="center" android:gravity="center"

View file

@ -11,6 +11,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="@integer/overScrollMode"
android:scrollbars="none" android:scrollbars="none"
android:transitionGroup="true" android:transitionGroup="true"
app:layout_dodgeInsetEdges="bottom" app:layout_dodgeInsetEdges="bottom"

View file

@ -46,7 +46,8 @@
android:scrollbars="none" android:scrollbars="none"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
tools:listitem="@layout/item_list" tools:listitem="@layout/item_list"
android:transitionGroup="true"/> android:overScrollMode="@integer/overScrollMode"
android:transitionGroup="true" />
<LinearLayout <LinearLayout
android:id="@android:id/empty" android:id="@android:id/empty"

View file

@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scrollView" android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:overScrollMode="@integer/overScrollMode">
<LinearLayout <LinearLayout
android:id="@+id/container" android:id="@+id/container"

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -7,49 +8,24 @@
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager" android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
android:overScrollMode="@integer/overScrollMode">
<FrameLayout </androidx.viewpager.widget.ViewPager>
android:id="@+id/playerLyrics"
<code.name.monkey.retromusic.lyrics.CoverLrcView
android:id="@+id/lyricsView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_margin="16dp"
android:alpha="0"
android:clipToPadding="false"
android:elevation="20dp"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:lrcLabel="@string/no_lyrics_found"
app:lrcNormalTextSize="28sp"
app:lrcPadding="24dp"
app:lrcTextGravity="center"
app:lrcTextSize="32sp"
app:lrcTimelineColor="@color/transparent"
tools:visibility="visible" />
<View
android:id="@+id/mask_lyrics"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/lyrics_mask" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/player_lyrics_line1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="16dp"
android:gravity="center"
android:shadowColor="@color/md_black_1000"
android:shadowRadius="4"
android:textAppearance="@style/TextViewHeadline5"
android:textColor="@color/md_white_1000"
android:visibility="gone" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/player_lyrics_line2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="16dp"
android:gravity="center"
android:shadowColor="@color/md_black_1000"
android:shadowRadius="4"
android:textAppearance="@style/TextViewHeadline5"
android:textColor="@color/md_white_1000" />
</FrameLayout>
</FrameLayout> </FrameLayout>

View file

@ -31,6 +31,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="@integer/overScrollMode"
android:paddingBottom="96dp" android:paddingBottom="96dp"
android:scrollbars="none" android:scrollbars="none"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"

View file

@ -35,7 +35,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="@integer/overScrollMode"
android:scrollbars="none" android:scrollbars="none"
android:layoutAnimation="@anim/layout_anim_fade"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
<LinearLayout <LinearLayout

View file

@ -73,7 +73,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp" android:padding="5dp"
android:scrollbarSize="0dp"> android:scrollbars="none"
android:overScrollMode="@integer/overScrollMode">
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/searchFilterGroup" android:id="@+id/searchFilterGroup"
@ -126,6 +127,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:overScrollMode="@integer/overScrollMode"
android:scrollbarStyle="outsideOverlay" android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />

View file

@ -66,7 +66,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" android:nestedScrollingEnabled="false"
tools:itemCount="10" android:overScrollMode="@integer/overScrollMode"
tools:itemCount="10"
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager" tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="@layout/item_album_card" tools:listitem="@layout/item_album_card"
tools:spanCount="3" /> tools:spanCount="3" />

View file

@ -4,5 +4,5 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?actionBarSize" android:layout_height="?actionBarSize"
android:elevation="@dimen/mcab_toolbar_elevation" android:elevation="@dimen/mcab_toolbar_elevation"
android:theme="@style/ThemeOverlay.Material3.ActionBar" android:theme="@style/mcab_theme"
tools:ignore="UnusedAttribute" /> tools:ignore="UnusedAttribute" />

View file

@ -41,6 +41,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" android:nestedScrollingEnabled="false"
android:overScrollMode="@integer/overScrollMode"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/clickable_area" app:layout_constraintTop_toBottomOf="@+id/clickable_area"

View file

@ -29,7 +29,6 @@
<item name="android:windowLightNavigationBar">false</item> <item name="android:windowLightNavigationBar">false</item>
<item name="colorSurface">@color/darkColorSurface</item> <item name="colorSurface">@color/darkColorSurface</item>
<item name="materialCardViewStyle">@style/Widget.MaterialComponents.CardView</item> <item name="materialCardViewStyle">@style/Widget.MaterialComponents.CardView</item>
<item name="bottomSheetTint">@color/bottomSheetColor</item>
<item name="elevationOverlayColor">@color/elevationOverlayDark</item> <item name="elevationOverlayColor">@color/elevationOverlayDark</item>
<item name="floatingActionButtonStyle"> <item name="floatingActionButtonStyle">
@style/Widget.MaterialComponents.FloatingActionButton @style/Widget.MaterialComponents.FloatingActionButton

View file

@ -27,7 +27,6 @@
<item name="bottomNavigationStyle">@style/Widget.Material3.BottomNavigationView</item> <item name="bottomNavigationStyle">@style/Widget.Material3.BottomNavigationView</item>
<item name="materialButtonStyle">@style/MaterialButtonTheme</item> <item name="materialButtonStyle">@style/MaterialButtonTheme</item>
<item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item> <item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item>
<item name="bottomSheetTint">@color/bottomSheetColor</item>
<item name="elevationOverlayColor">@color/elevationOverlay</item> <item name="elevationOverlayColor">@color/elevationOverlay</item>
<item name="floatingActionButtonStyle"> <item name="floatingActionButtonStyle">
@style/Widget.MaterialComponents.FloatingActionButton @style/Widget.MaterialComponents.FloatingActionButton
@ -54,7 +53,6 @@
<item name="bottomNavigationStyle">@style/Widget.Material3.BottomNavigationView</item> <item name="bottomNavigationStyle">@style/Widget.Material3.BottomNavigationView</item>
<item name="android:windowBackground">@color/window_color_dark</item> <item name="android:windowBackground">@color/window_color_dark</item>
<item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item> <item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item>
<item name="bottomSheetTint">@color/bottomSheetColor</item>
<item name="elevationOverlayColor">@color/elevationOverlay</item> <item name="elevationOverlayColor">@color/elevationOverlay</item>
<item name="floatingActionButtonStyle"> <item name="floatingActionButtonStyle">
@style/Widget.MaterialComponents.FloatingActionButton @style/Widget.MaterialComponents.FloatingActionButton
@ -78,7 +76,6 @@
<item name="materialButtonStyle">@style/MaterialButtonTheme</item> <item name="materialButtonStyle">@style/MaterialButtonTheme</item>
<item name="bottomNavigationStyle">@style/Widget.Material3.BottomNavigationView</item> <item name="bottomNavigationStyle">@style/Widget.Material3.BottomNavigationView</item>
<item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item> <item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item>
<item name="bottomSheetTint">@color/bottomSheetColorLight</item>
<item name="elevationOverlayColor">@color/elevationOverlayLight</item> <item name="elevationOverlayColor">@color/elevationOverlayLight</item>
<item name="floatingActionButtonStyle"> <item name="floatingActionButtonStyle">
@style/Widget.MaterialComponents.FloatingActionButton @style/Widget.MaterialComponents.FloatingActionButton

View file

@ -4,4 +4,5 @@
<bool name="md3_enabled">true</bool> <bool name="md3_enabled">true</bool>
<bool name="colored_notification_available">false</bool> <bool name="colored_notification_available">false</bool>
<integer name="overScrollMode">0</integer>
</resources> </resources>

View file

@ -6,7 +6,6 @@
<attr name="defaultFooterColor" format="color" /> <attr name="defaultFooterColor" format="color" />
<attr name="toolbarPopupTheme" format="reference" /> <attr name="toolbarPopupTheme" format="reference" />
<attr name="lineHeightHint" format="dimension" /> <attr name="lineHeightHint" format="dimension" />
<attr name="bottomSheetTint" format="reference" />
<declare-styleable name="NetworkImageView"> <declare-styleable name="NetworkImageView">
<attr name="url_link" format="string" /> <attr name="url_link" format="string" />

View file

@ -28,4 +28,6 @@
<bool name="md3_enabled">false</bool> <bool name="md3_enabled">false</bool>
<bool name="colored_notification_available">true</bool> <bool name="colored_notification_available">true</bool>
<integer name="overScrollMode">2</integer>
<bool name="allowBackup">true</bool>
</resources> </resources>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<integer name="lrc_animation_duration">1000</integer> <integer name="lrc_animation_duration">1000</integer>
<dimen name="lrc_text_size">16sp</dimen> <dimen name="lrc_text_size">20sp</dimen>
<dimen name="lrc_time_text_size">12sp</dimen> <dimen name="lrc_time_text_size">16sp</dimen>
<dimen name="lrc_divider_height">16dp</dimen> <dimen name="lrc_divider_height">16dp</dimen>
<dimen name="lrc_timeline_height">1dp</dimen> <dimen name="lrc_timeline_height">1dp</dimen>
<dimen name="lrc_drawable_width">30dp</dimen> <dimen name="lrc_drawable_width">30dp</dimen>

View file

@ -73,6 +73,7 @@
<string name="app_widget_big_name">Full Image</string> <string name="app_widget_big_name">Full Image</string>
<string name="app_widget_card_name">Card</string> <string name="app_widget_card_name">Card</string>
<string name="app_widget_classic_name">Classic</string> <string name="app_widget_classic_name">Classic</string>
<string name="app_widget_md3_name">MD3</string>
<string name="app_widget_small_name">Small</string> <string name="app_widget_small_name">Small</string>
<string name="app_widget_text_name">Minimal Text</string> <string name="app_widget_text_name">Minimal Text</string>
<string name="artist">Artist</string> <string name="artist">Artist</string>
@ -81,6 +82,7 @@
<string name="audio_focus_denied">Audio focus denied.</string> <string name="audio_focus_denied">Audio focus denied.</string>
<string name="audio_settings_summary">Change the sound settings and adjust the equalizer controls</string> <string name="audio_settings_summary">Change the sound settings and adjust the equalizer controls</string>
<string name="auto">Auto</string> <string name="auto">Auto</string>
<string name="backup_restore_settings_summary">Backup and restore your settings, playlists</string>
<string name="backup_restore_title"> <string name="backup_restore_title">
<![CDATA[Backup & Restore]]> <![CDATA[Backup & Restore]]>
</string> </string>
@ -113,6 +115,7 @@
<string name="cascading">Cascading</string> <string name="cascading">Cascading</string>
<string name="changelog">Changelog</string> <string name="changelog">Changelog</string>
<string name="changelog_summary">Check out What\'s New</string> <string name="changelog_summary">Check out What\'s New</string>
<string name="choose_restore_title">Choose what to restore</string>
<string name="circle">Circle</string> <string name="circle">Circle</string>
<string name="circular">Circular</string> <string name="circular">Circular</string>
<string name="classic">Classic</string> <string name="classic">Classic</string>
@ -132,7 +135,9 @@
<string name="created_playlist_x">Created playlist %1$s.</string> <string name="created_playlist_x">Created playlist %1$s.</string>
<string name="credit_title">Members and contributors </string> <string name="credit_title">Members and contributors </string>
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string> <string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
<string name="custom_artist_images">Custom Artist Images</string>
<string name="dark_theme_name">Kinda Dark</string> <string name="dark_theme_name">Kinda Dark</string>
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
<string name="delete_playlist_title">Delete playlist</string> <string name="delete_playlist_title">Delete playlist</string>
<string name="delete_playlist_x"> <string name="delete_playlist_x">
<![CDATA[Delete the playlist <b>%1$s</b>?]]> <![CDATA[Delete the playlist <b>%1$s</b>?]]>
@ -156,6 +161,7 @@
<string name="device_info">Device info</string> <string name="device_info">Device info</string>
<string name="dialog_message_set_ringtone">Allow Retro Music to modify audio settings</string> <string name="dialog_message_set_ringtone">Allow Retro Music to modify audio settings</string>
<string name="dialog_title_set_ringtone">Set ringtone</string> <string name="dialog_title_set_ringtone">Set ringtone</string>
<string name="disc_hint">Disc Number</string>
<string name="do_you_want_to_clear_the_blacklist">Do you want to clear the blacklist?</string> <string name="do_you_want_to_clear_the_blacklist">Do you want to clear the blacklist?</string>
<string name="do_you_want_to_remove_from_the_blacklist"> <string name="do_you_want_to_remove_from_the_blacklist">
<![CDATA[Do you want to remove <b>%1$s</b> from the blacklist?]]> <![CDATA[Do you want to remove <b>%1$s</b> from the blacklist?]]>
@ -404,6 +410,7 @@
<string name="reset_action">Reset</string> <string name="reset_action">Reset</string>
<string name="reset_artist_image">Reset artist image</string> <string name="reset_artist_image">Reset artist image</string>
<string name="restore">Restore</string> <string name="restore">Restore</string>
<string name="restore_message">Do you want to restore backup?</string>
<string name="restored_previous_purchase_please_restart">Restored previous purchase. Please restart the app to make use of all features.</string> <string name="restored_previous_purchase_please_restart">Restored previous purchase. Please restart the app to make use of all features.</string>
<string name="restored_previous_purchases">Restored previous purchases.</string> <string name="restored_previous_purchases">Restored previous purchases.</string>
<string name="restoring_purchase">Restoring purchase…</string> <string name="restoring_purchase">Restoring purchase…</string>
@ -453,8 +460,8 @@
<string name="sort_order">Sort order</string> <string name="sort_order">Sort order</string>
<string name="sort_order_a_z">Ascending</string> <string name="sort_order_a_z">Ascending</string>
<string name="sort_order_album">Album</string> <string name="sort_order_album">Album</string>
<string name="sort_order_artist">Artist</string>
<string name="sort_order_album_artist">@string/album_artist</string> <string name="sort_order_album_artist">@string/album_artist</string>
<string name="sort_order_artist">Artist</string>
<string name="sort_order_composer">Composer</string> <string name="sort_order_composer">Composer</string>
<string name="sort_order_date">Date added</string> <string name="sort_order_date">Date added</string>
<string name="sort_order_date_modified">Date modified</string> <string name="sort_order_date_modified">Date modified</string>
@ -480,6 +487,7 @@
<string name="tiny">Tiny</string> <string name="tiny">Tiny</string>
<string name="tiny_card_style">Tiny card</string> <string name="tiny_card_style">Tiny card</string>
<string name="title">Title</string> <string name="title">Title</string>
<string name="title_new_backup">New Backup</string>
<string name="today">Today</string> <string name="today">Today</string>
<string name="top_albums">Top albums</string> <string name="top_albums">Top albums</string>
<string name="top_artists">Top artists</string> <string name="top_artists">Top artists</string>
@ -495,6 +503,7 @@
<string name="up_next">Up next</string> <string name="up_next">Up next</string>
<string name="update_image">Update image</string> <string name="update_image">Update image</string>
<string name="updating">Updating…</string> <string name="updating">Updating…</string>
<string name="user_images_description">User Images</string>
<string name="user_name">User Name</string> <string name="user_name">User Name</string>
<string name="username">Username</string> <string name="username">Username</string>
<string name="version">Version</string> <string name="version">Version</string>
@ -515,8 +524,4 @@
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string> <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="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="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
<string name="restore_message">Do you want to restore backup?</string>
<string name="title_new_backup">New Backup</string>
<string name="backup_restore_settings_summary">Backup and restore your settings, playlists</string>
<string name="app_widget_md3_name">MD3</string>
</resources> </resources>

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