Merge pull request #1471 from RetroMusicPlayer/dev-alpha

Android 13 API changes
This commit is contained in:
Daksh P. Jain 2023-03-29 22:03:01 +05:30 committed by GitHub
commit 8024700b7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
84 changed files with 540 additions and 606 deletions

View file

@ -10,13 +10,13 @@ android {
defaultConfig { defaultConfig {
minSdk 21 minSdk 21
targetSdk 32 targetSdk 33
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
applicationId namespace applicationId namespace
versionCode 10600 versionCode 10602
versionName '6.0.4' versionName '6.1.0'
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"") buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
} }
@ -112,7 +112,7 @@ dependencies {
implementation 'androidx.mediarouter:mediarouter:1.3.1' implementation 'androidx.mediarouter:mediarouter:1.3.1'
//Cast Dependencies //Cast Dependencies
normalImplementation 'com.google.android.gms:play-services-cast-framework:21.2.0' normalImplementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
//WebServer by NanoHttpd //WebServer by NanoHttpd
normalImplementation "org.nanohttpd:nanohttpd:2.3.1" normalImplementation "org.nanohttpd:nanohttpd:2.3.1"
@ -120,7 +120,7 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version" implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version" implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
def room_version = '2.5.0' def room_version = '2.5.1'
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version" implementation "androidx.room:room-ktx:$room_version"
ksp "androidx.room:room-compiler:$room_version" ksp "androidx.room:room-compiler:$room_version"
@ -150,11 +150,11 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
def koin_version = '3.3.3' def koin_version = '3.4.0'
implementation "io.insert-koin:koin-core:$koin_version" implementation "io.insert-koin:koin-core:$koin_version"
implementation "io.insert-koin:koin-android:$koin_version" implementation "io.insert-koin:koin-android:$koin_version"
def glide_version = '4.15.0' def glide_version = '4.15.1'
implementation "com.github.bumptech.glide:glide:$glide_version" implementation "com.github.bumptech.glide:glide:$glide_version"
ksp "com.github.bumptech.glide:ksp:$glide_version" ksp "com.github.bumptech.glide:ksp:$glide_version"
implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version" implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version"
@ -176,5 +176,5 @@ dependencies {
implementation 'com.github.dhaval2404:imagepicker:2.1' implementation 'com.github.dhaval2404:imagepicker:2.1'
implementation 'me.zhanghai.android.fastscroll:library:1.2.0' implementation 'me.zhanghai.android.fastscroll:library:1.2.0'
implementation 'cat.ereza:customactivityoncrash:2.4.0' implementation 'cat.ereza:customactivityoncrash:2.4.0'
implementation 'me.tankery.lib:circularSeekBar:1.4.1' implementation 'me.tankery.lib:circularSeekBar:1.4.2'
} }

View file

@ -8,8 +8,8 @@ import androidx.fragment.app.FragmentActivity
fun Context.setUpMediaRouteButton(menu: Menu) {} fun Context.setUpMediaRouteButton(menu: Menu) {}
fun FragmentActivity.installLanguageAndRecreate(code: String) { fun FragmentActivity.installLanguageAndRecreate(code: String, onInstallComplete: () -> Unit) {
recreate() onInstallComplete()
} }
fun Context.goToProVersion() {} fun Context.goToProVersion() {}

View file

@ -9,7 +9,11 @@
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" /> android:maxSdkVersion="29" />
@ -30,16 +34,19 @@
<application <application
android:name=".App" android:name=".App"
android:allowBackup="@bool/allowBackup" android:allowBackup="@bool/allowBackup"
android:appCategory="audio"
android:configChanges="locale|layoutDirection" android:configChanges="locale|layoutDirection"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:localeConfig="@xml/locales_config"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:restoreAnyVersion="true" android:restoreAnyVersion="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.RetroMusic.FollowSystem" android:theme="@style/Theme.RetroMusic.FollowSystem"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:targetApi="m"> tools:ignore="UnusedAttribute">
<activity <activity
android:name=".activities.MainActivity" android:name=".activities.MainActivity"
android:exported="true" android:exported="true"
@ -296,7 +303,6 @@
<service <service
android:name=".service.MusicService" android:name=".service.MusicService"
android:enabled="true"
android:exported="true" android:exported="true"
android:foregroundServiceType="mediaPlayback" android:foregroundServiceType="mediaPlayback"
android:label="@string/app_name"> android:label="@string/app_name">
@ -323,6 +329,16 @@
<meta-data <meta-data
android:name="com.google.android.gms.car.notification.SmallIcon" android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification" /> android:resource="@drawable/ic_notification" />
<!-- For auto-storage of locale on Android 12 and lower -->
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
</application> </application>
<!-- <!--

View file

@ -71,6 +71,19 @@
The app heavily relies on Kotlin. The app heavily relies on Kotlin.
</p> </p>
</div> </div>
<div>
<h5>March 30, 2023</h5>
<h2>v6.1.0</h2>
<h3>What's New</h3>
<ul>
<li>App now targets Android 13, support for Granular media permissions, Photo picker, Per-app language preferences & Predictive back gesture</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Fixed playlist reordering crash</li>
<li>Other minor bugs fixes and improvements</li>
</ul>
</div>
<div> <div>
<h5>March 13, 2023</h5> <h5>March 13, 2023</h5>
<h2>v6.0.4</h2> <h2>v6.0.4</h2>

View file

@ -96,6 +96,7 @@ const val ALBUM_COVER_STYLE = "album_cover_style_id"
const val ALBUM_COVER_TRANSFORM = "album_cover_transform" const val ALBUM_COVER_TRANSFORM = "album_cover_transform"
const val TAB_TEXT_MODE = "tab_text_mode" const val TAB_TEXT_MODE = "tab_text_mode"
const val LANGUAGE_NAME = "language_name" const val LANGUAGE_NAME = "language_name"
const val LOCALE_AUTO_STORE_ENABLED = "locale_auto_store_enabled"
const val SLEEP_TIMER_FINISH_SONG = "sleep_timer_finish_song" const val SLEEP_TIMER_FINISH_SONG = "sleep_timer_finish_song"
const val ALBUM_GRID_STYLE = "album_grid_style_home" const val ALBUM_GRID_STYLE = "album_grid_style_home"
const val ARTIST_GRID_STYLE = "artist_grid_style_home" const val ARTIST_GRID_STYLE = "artist_grid_style_home"
@ -119,7 +120,6 @@ const val ARTIST_GRID_SIZE_LAND = "artist_grid_size_land"
const val PLAYLIST_GRID_SIZE = "playlist_grid_size" const val PLAYLIST_GRID_SIZE = "playlist_grid_size"
const val PLAYLIST_GRID_SIZE_LAND = "playlist_grid_size_land" const val PLAYLIST_GRID_SIZE_LAND = "playlist_grid_size_land"
const val COLORED_APP_SHORTCUTS = "colored_app_shortcuts" const val COLORED_APP_SHORTCUTS = "colored_app_shortcuts"
const val AUDIO_DUCKING = "audio_ducking"
const val LAST_ADDED_CUTOFF = "last_added_interval" const val LAST_ADDED_CUTOFF = "last_added_interval"
const val LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value" const val LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value"
const val NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time" const val NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time"

View file

@ -1,25 +0,0 @@
package code.name.monkey.retromusic
import android.content.Context
import android.content.ContextWrapper
import android.os.LocaleList
import code.name.monkey.appthemehelper.util.VersionUtils.hasNougat
import java.util.*
class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
companion object {
fun wrap(context: Context?, newLocale: Locale?): LanguageContextWrapper {
if (context == null) return LanguageContextWrapper(context)
val configuration = context.resources.configuration
if (hasNougat()) {
configuration.setLocale(newLocale)
val localeList = LocaleList(newLocale)
LocaleList.setDefault(localeList)
configuration.setLocales(localeList)
} else {
configuration.setLocale(newLocale)
}
return LanguageContextWrapper(context.createConfigurationContext(configuration))
}
}
}

View file

@ -4,7 +4,6 @@ import androidx.room.Room
import code.name.monkey.retromusic.auto.AutoMusicProvider import code.name.monkey.retromusic.auto.AutoMusicProvider
import code.name.monkey.retromusic.cast.RetroWebServer import code.name.monkey.retromusic.cast.RetroWebServer
import code.name.monkey.retromusic.db.MIGRATION_23_24 import code.name.monkey.retromusic.db.MIGRATION_23_24
import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.RetroDatabase import code.name.monkey.retromusic.db.RetroDatabase
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel

View file

@ -14,7 +14,6 @@
*/ */
package code.name.monkey.retromusic.activities package code.name.monkey.retromusic.activities
import android.Manifest
import android.Manifest.permission.BLUETOOTH_CONNECT import android.Manifest.permission.BLUETOOTH_CONNECT
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -129,10 +128,7 @@ class PermissionActivity : AbsMusicServiceActivity() {
} }
private fun hasStoragePermission(): Boolean { private fun hasStoragePermission(): Boolean {
return ActivityCompat.checkSelfPermission( return hasPermissions()
this,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
} }
@RequiresApi(Build.VERSION_CODES.S) @RequiresApi(Build.VERSION_CODES.S)

View file

@ -11,7 +11,6 @@ import android.view.ViewGroup
import android.webkit.WebResourceRequest import android.webkit.WebResourceRequest
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import androidx.core.content.PackageManagerCompat
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
@ -27,7 +26,7 @@ import code.name.monkey.retromusic.extensions.openUrl
import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.Locale import java.util.*
class WhatsNewFragment : BottomSheetDialogFragment() { class WhatsNewFragment : BottomSheetDialogFragment() {
private var _binding: FragmentWhatsNewBinding? = null private var _binding: FragmentWhatsNewBinding? = null

View file

@ -18,6 +18,7 @@ import android.Manifest
import android.content.* import android.content.*
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
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
@ -96,8 +97,7 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
filter.addAction(MEDIA_STORE_CHANGED) filter.addAction(MEDIA_STORE_CHANGED)
filter.addAction(FAVORITE_STATE_CHANGED) filter.addAction(FAVORITE_STATE_CHANGED)
registerReceiver(musicStateReceiver, filter) ContextCompat.registerReceiver(this, musicStateReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
receiverRegistered = true receiverRegistered = true
} }
@ -190,7 +190,13 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
} }
override fun getPermissionsToRequest(): Array<String> { override fun getPermissionsToRequest(): Array<String> {
return mutableListOf(Manifest.permission.READ_EXTERNAL_STORAGE).apply { return mutableListOf<String>().apply {
if (VersionUtils.hasT()) {
add(Manifest.permission.READ_MEDIA_AUDIO)
add(Manifest.permission.POST_NOTIFICATIONS)
} else {
add(Manifest.permission.READ_EXTERNAL_STORAGE)
}
if (!VersionUtils.hasR()) { if (!VersionUtils.hasR()) {
add(Manifest.permission.WRITE_EXTERNAL_STORAGE) add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} }

View file

@ -138,6 +138,16 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
private var navigationBarColorAnimator: ValueAnimator? = null private var navigationBarColorAnimator: ValueAnimator? = null
private val argbEvaluator: ArgbEvaluator = ArgbEvaluator() private val argbEvaluator: ArgbEvaluator = ArgbEvaluator()
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
println("Handle back press ${bottomSheetBehavior.state}")
if (!handleBackPress()) {
remove()
onBackPressedDispatcher.onBackPressed()
}
}
}
private val bottomSheetCallbackList by lazy { private val bottomSheetCallbackList by lazy {
object : BottomSheetCallback() { object : BottomSheetCallback() {
@ -154,6 +164,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
} }
override fun onStateChanged(bottomSheet: View, newState: Int) { override fun onStateChanged(bottomSheet: View, newState: Int) {
onBackPressedCallback.isEnabled = newState == STATE_EXPANDED
when (newState) { when (newState) {
STATE_EXPANDED -> { STATE_EXPANDED -> {
onPanelExpanded() onPanelExpanded()
@ -212,6 +223,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
} }
navigationBarColor = surfaceColor() navigationBarColor = surfaceColor()
onBackPressedDispatcher.addCallback(onBackPressedCallback)
} }
private fun setupBottomSheet() { private fun setupBottomSheet() {
@ -231,14 +244,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
if (bottomSheetBehavior.state == STATE_EXPANDED) { if (bottomSheetBehavior.state == STATE_EXPANDED) {
setMiniPlayerAlphaProgress(1f) setMiniPlayerAlphaProgress(1f)
} }
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (!handleBackPress()) {
remove()
onBackPressedDispatcher.onBackPressed()
}
}
})
} }
override fun onDestroy() { override fun onDestroy() {
@ -403,7 +408,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
} }
private fun handleBackPress(): Boolean { private fun handleBackPress(): Boolean {
if (bottomSheetBehavior.peekHeight != 0 && playerFragment.onBackPressed()) return true
if (panelState == STATE_EXPANDED) { if (panelState == STATE_EXPANDED) {
collapsePanel() collapsePanel()
return true return true

View file

@ -15,29 +15,28 @@
package code.name.monkey.retromusic.activities.base package code.name.monkey.retromusic.activities.base
import android.content.Context import android.content.Context
import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.KeyEvent import android.view.KeyEvent
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.core.os.ConfigurationCompat import androidx.core.os.LocaleListCompat
import code.name.monkey.appthemehelper.common.ATHToolbarActivity import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.LanguageContextWrapper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.maybeShowAnnoyingToasts import code.name.monkey.retromusic.util.maybeShowAnnoyingToasts
import code.name.monkey.retromusic.util.theme.getNightMode import code.name.monkey.retromusic.util.theme.getNightMode
import code.name.monkey.retromusic.util.theme.getThemeResValue import code.name.monkey.retromusic.util.theme.getThemeResValue
import java.util.*
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable { abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
updateLocale()
updateTheme() updateTheme()
hideStatusBar() hideStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -62,6 +61,14 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
} }
} }
private fun updateLocale() {
val localeCode = PreferenceUtil.languageCode
if (PreferenceUtil.isLocaleAutoStorageEnabled) {
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(localeCode))
PreferenceUtil.isLocaleAutoStorageEnabled = true
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) { override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus) super.onWindowFocusChanged(hasFocus)
if (hasFocus) { if (hasFocus) {
@ -96,14 +103,7 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
} }
override fun attachBaseContext(newBase: Context?) { override fun attachBaseContext(newBase: Context?) {
val code = PreferenceUtil.languageCode super.attachBaseContext(newBase)
val locale = if (code == "auto") {
// Get the device default locale
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0]
} else {
Locale.forLanguageTag(code)
}
super.attachBaseContext(LanguageContextWrapper.wrap(newBase, locale))
installSplitCompat() installSplitCompat()
} }
} }

View file

@ -5,10 +5,10 @@ import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import androidx.annotation.IntRange import androidx.annotation.IntRange
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.PreferenceUtil.isAdaptiveColor import code.name.monkey.retromusic.util.PreferenceUtil.isAdaptiveColor
import code.name.monkey.retromusic.util.PreferenceUtil.languageCode
import code.name.monkey.retromusic.util.PreferenceUtil.nowPlayingScreen import code.name.monkey.retromusic.util.PreferenceUtil.nowPlayingScreen
import java.util.* import java.util.*
@ -106,6 +106,6 @@ class DeviceInfo(context: Context) {
baseTheme = PreferenceUtil.baseTheme baseTheme = PreferenceUtil.baseTheme
nowPlayingTheme = context.getString(nowPlayingScreen.titleRes) nowPlayingTheme = context.getString(nowPlayingScreen.titleRes)
isAdaptive = isAdaptiveColor isAdaptive = isAdaptiveColor
selectedLang = languageCode selectedLang = AppCompatDelegate.getApplicationLocales().toLanguageTags()
} }
} }

View file

@ -29,6 +29,7 @@ import android.view.animation.OvershootInterpolator
import android.widget.ImageView import android.widget.ImageView
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -37,7 +38,6 @@ import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.R.drawable import code.name.monkey.retromusic.R.drawable
import code.name.monkey.retromusic.activities.base.AbsBaseActivity import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.colorButtons import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.hideSoftKeyboard import code.name.monkey.retromusic.extensions.hideSoftKeyboard
@ -45,8 +45,8 @@ import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
import code.name.monkey.retromusic.model.ArtworkInfo import code.name.monkey.retromusic.model.ArtworkInfo
import code.name.monkey.retromusic.model.AudioTagInfo import code.name.monkey.retromusic.model.AudioTagInfo
import code.name.monkey.retromusic.repository.Repository import code.name.monkey.retromusic.repository.Repository
import code.name.monkey.retromusic.util.SAFUtil
import code.name.monkey.retromusic.util.logD import code.name.monkey.retromusic.util.logD
import code.name.monkey.retromusic.util.logE
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -56,7 +56,6 @@ import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.io.File import java.io.File
import java.util.Collections
abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() { abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
abstract val editorImage: ImageView abstract val editorImage: ImageView
@ -100,7 +99,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -109,7 +109,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -117,7 +118,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -126,7 +128,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -135,7 +138,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -144,7 +148,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -153,7 +158,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -162,7 +168,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -171,7 +178,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -180,7 +188,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -189,7 +198,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
null null
} }
} }
@ -207,11 +217,17 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
) )
} }
return null return null
} catch (ignored: Exception) { } catch (e: Exception) {
logE(e)
return null return null
} }
} }
private val pickArtworkImage =
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
loadImageFromFile(uri)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
_binding = bindingInflater.invoke(layoutInflater) _binding = bindingInflater.invoke(layoutInflater)
@ -252,14 +268,7 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
} }
private fun startImagePicker() { private fun startImagePicker() {
val intent = Intent(Intent.ACTION_GET_CONTENT) pickArtworkImage.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
intent.type = "image/*"
startActivityForResult(
Intent.createChooser(
intent,
getString(R.string.pick_from_local_storage)
), REQUEST_CODE_SELECT_IMAGE
)
} }
protected abstract fun loadCurrentImage() protected abstract fun loadCurrentImage()
@ -360,9 +369,12 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
artworkInfo artworkInfo
) )
) )
val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build()) if (cacheFiles.isNotEmpty()) {
val pendingIntent =
MediaStore.createWriteRequest(contentResolver, getSongUris())
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
}
} else { } else {
TagWriter.writeTagsToFiles( TagWriter.writeTagsToFiles(
this@AbsTagEditorActivity, AudioTagInfo( this@AbsTagEditorActivity, AudioTagInfo(
@ -400,39 +412,14 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
} }
} }
private lateinit var audioFile: AudioFile
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
when (requestCode) {
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
intent?.data?.let {
loadImageFromFile(it)
}
}
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
SAFUtil.openTreePicker(this)
}
SAFUtil.REQUEST_SAF_PICK_TREE -> {
if (resultCode == Activity.RESULT_OK) {
SAFUtil.saveTreeUri(this, intent)
writeTags(savedSongPaths)
}
}
SAFUtil.REQUEST_SAF_PICK_FILE -> {
if (resultCode == Activity.RESULT_OK) {
writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent!!.dataString))
}
}
}
}
private fun getAudioFile(path: String): AudioFile { private fun getAudioFile(path: String): AudioFile {
return try { return try {
AudioFileIO.read(File(path)) if (!this::audioFile.isInitialized) {
audioFile = AudioFileIO.read(File(path))
}
audioFile
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Could not read audio file $path", e) Log.e(TAG, "Could not read audio file $path", e)
AudioFile() AudioFile()

View file

@ -30,11 +30,7 @@ import androidx.core.widget.doAfterTextChanged
import code.name.monkey.appthemehelper.util.MaterialValueHelper 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.*
import code.name.monkey.retromusic.extensions.defaultFooterColor
import code.name.monkey.retromusic.extensions.isColorLight
import code.name.monkey.retromusic.extensions.setTint
import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.model.ArtworkInfo import code.name.monkey.retromusic.model.ArtworkInfo
@ -50,7 +46,7 @@ import com.bumptech.glide.request.target.ImageViewTarget
import com.bumptech.glide.request.transition.Transition 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 java.util.EnumMap import java.util.*
class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBinding>() { class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBinding>() {

View file

@ -7,6 +7,7 @@ import android.media.MediaScannerConnection
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener
import code.name.monkey.retromusic.model.AudioTagInfo import code.name.monkey.retromusic.model.AudioTagInfo
@ -20,6 +21,7 @@ import org.jaudiotagger.audio.exceptions.CannotReadException
import org.jaudiotagger.audio.exceptions.CannotWriteException 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.FieldDataInvalidException
import org.jaudiotagger.tag.TagException import org.jaudiotagger.tag.TagException
import org.jaudiotagger.tag.images.AndroidArtwork import org.jaudiotagger.tag.images.AndroidArtwork
import org.jaudiotagger.tag.images.Artwork import org.jaudiotagger.tag.images.Artwork
@ -31,9 +33,9 @@ class TagWriter {
companion object { companion object {
suspend fun scan(context: Context, toBeScanned: List<String?>?) { suspend fun scan(context: Context, toBeScanned: List<String?>?) {
if (toBeScanned == null || toBeScanned.isEmpty()) { if (toBeScanned.isNullOrEmpty()) {
Log.i("scan", "scan: Empty") Log.i("scan", "scan: Empty")
context.showToast( "Scan file from folder") context.showToast("Scan file from folder")
return return
} }
MediaScannerConnection.scanFile( MediaScannerConnection.scanFile(
@ -50,150 +52,151 @@ class TagWriter {
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) { suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { var artwork: Artwork? = null
var artwork: Artwork? = null var albumArtFile: File? = null
var albumArtFile: File? = null if (info.artworkInfo?.artwork != null) {
if (info.artworkInfo?.artwork != null) { try {
try { albumArtFile = createAlbumArtFile(context).canonicalFile
albumArtFile = createAlbumArtFile(context).canonicalFile info.artworkInfo.artwork.compress(
info.artworkInfo.artwork.compress( Bitmap.CompressFormat.JPEG,
Bitmap.CompressFormat.JPEG, 100,
100, albumArtFile.outputStream()
albumArtFile.outputStream() )
) artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile) } catch (e: IOException) {
} catch (e: IOException) { e.printStackTrace()
e.printStackTrace()
}
} }
var wroteArtwork = false
var deletedArtwork = false
for (filePath in info.filePaths!!) {
try {
val audioFile = AudioFileIO.read(File(filePath))
val tag = audioFile.tagOrCreateAndSetDefault
if (info.fieldKeyValueMap != null) {
for ((key, value) in info.fieldKeyValueMap) {
try {
tag.setField(key, value)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
}
}
if (wroteArtwork) {
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
} else if (deletedArtwork) {
deleteAlbumArt(context, info.artworkInfo!!.albumId)
}
scan(context, info.filePaths)
}.onFailure {
it.printStackTrace()
} }
var wroteArtwork = false
var deletedArtwork = false
for (filePath in info.filePaths!!) {
try {
val audioFile = AudioFileIO.read(File(filePath))
val tag = audioFile.tagOrCreateAndSetDefault
if (info.fieldKeyValueMap != null) {
for ((key, value) in info.fieldKeyValueMap) {
try {
tag.setField(key, value)
} catch (e: FieldDataInvalidException) {
withContext(Dispatchers.Main) {
context.showToast(R.string.could_not_write_tags_to_file)
}
return@withContext listOf<File>()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
}
}
if (wroteArtwork) {
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
} else if (deletedArtwork) {
deleteAlbumArt(context, info.artworkInfo!!.albumId)
}
scan(context, info.filePaths)
} }
} }
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> = suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val cacheFiles = mutableListOf<File>() val cacheFiles = mutableListOf<File>()
runCatching { var artwork: Artwork? = null
var artwork: Artwork? = null var albumArtFile: File? = null
var albumArtFile: File? = null if (info.artworkInfo?.artwork != null) {
if (info.artworkInfo?.artwork != null) { try {
try { albumArtFile = createAlbumArtFile(context).canonicalFile
albumArtFile = createAlbumArtFile(context).canonicalFile info.artworkInfo.artwork.compress(
info.artworkInfo.artwork.compress( Bitmap.CompressFormat.JPEG,
Bitmap.CompressFormat.JPEG, 100,
100, albumArtFile.outputStream()
albumArtFile.outputStream() )
) artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile) } catch (e: IOException) {
} catch (e: IOException) { e.printStackTrace()
e.printStackTrace()
}
} }
var wroteArtwork = false }
var deletedArtwork = false var wroteArtwork = false
for (filePath in info.filePaths!!) { var deletedArtwork = false
try { for (filePath in info.filePaths!!) {
val originFile = File(filePath) try {
val cacheFile = File(context.cacheDir, originFile.name) val originFile = File(filePath)
cacheFiles.add(cacheFile) val cacheFile = File(context.cacheDir, originFile.name)
originFile.inputStream().use { input -> cacheFiles.add(cacheFile)
cacheFile.outputStream().use { output -> originFile.inputStream().use { input ->
input.copyTo(output) cacheFile.outputStream().use { output ->
} input.copyTo(output)
} }
val audioFile = AudioFileIO.read(cacheFile) }
val tag = audioFile.tagOrCreateAndSetDefault val audioFile = AudioFileIO.read(cacheFile)
if (info.fieldKeyValueMap != null) { val tag = audioFile.tagOrCreateAndSetDefault
for ((key, value) in info.fieldKeyValueMap) { if (info.fieldKeyValueMap != null) {
try { for ((key, value) in info.fieldKeyValueMap) {
tag.setField(key, value) try {
} catch (e: Exception) { tag.setField(key, value)
e.printStackTrace() } catch (e: FieldDataInvalidException) {
withContext(Dispatchers.Main) {
context.showToast(R.string.could_not_write_tags_to_file)
} }
return@withContext listOf<File>()
} catch (e: Exception) {
e.printStackTrace()
} }
} }
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
} }
if (info.artworkInfo != null) {
if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField()
deletedArtwork = true
} else if (artwork != null) {
tag.deleteArtworkField()
tag.setField(artwork)
wroteArtwork = true
}
}
audioFile.commit()
} catch (e: CannotReadException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: CannotWriteException) {
e.printStackTrace()
} catch (e: TagException) {
e.printStackTrace()
} catch (e: ReadOnlyFileException) {
e.printStackTrace()
} catch (e: InvalidAudioFrameException) {
e.printStackTrace()
} }
if (wroteArtwork) { }
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path) if (wroteArtwork) {
} else if (deletedArtwork) { insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
deleteAlbumArt(context, info.artworkInfo!!.albumId) } else if (deletedArtwork) {
} deleteAlbumArt(context, info.artworkInfo!!.albumId)
}.onFailure {
it.printStackTrace()
} }
cacheFiles cacheFiles
} }

View file

@ -27,13 +27,7 @@ import androidx.lifecycle.lifecycleScope
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.fragments.AlbumCoverStyle import code.name.monkey.retromusic.fragments.AlbumCoverStyle
import code.name.monkey.retromusic.fragments.NowPlayingScreen.Card import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.fragments.NowPlayingScreen.Classic
import code.name.monkey.retromusic.fragments.NowPlayingScreen.Fit
import code.name.monkey.retromusic.fragments.NowPlayingScreen.Full
import code.name.monkey.retromusic.fragments.NowPlayingScreen.Gradient
import code.name.monkey.retromusic.fragments.NowPlayingScreen.Peek
import code.name.monkey.retromusic.fragments.NowPlayingScreen.Tiny
import code.name.monkey.retromusic.fragments.base.goToLyrics import code.name.monkey.retromusic.fragments.base.goToLyrics
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette

View file

@ -26,6 +26,7 @@ import code.name.monkey.retromusic.db.toSongsEntity
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.ViewUtil
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -50,11 +51,7 @@ class OrderablePlaylistSongAdapter(
override fun getItemId(position: Int): Long { override fun getItemId(position: Int): Long {
// requires static value, it means need to keep the same value // requires static value, it means need to keep the same value
// even if the item position has been changed. // even if the item position has been changed.
return if (position != 0) { return dataSet[position].id
dataSet[position - 1].id
} else {
-1
}
} }
override fun createViewHolder(view: View): SongAdapter.ViewHolder { override fun createViewHolder(view: View): SongAdapter.ViewHolder {
@ -99,26 +96,22 @@ class OrderablePlaylistSongAdapter(
} }
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean { override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
if (dataSet.size == 0 or 1 || isInQuickSelectMode) { if (isInQuickSelectMode) {
return false return false
} }
val dragHandle = holder.dragView ?: return false return ViewUtil.hitTest(holder.imageText!!, x, y) || ViewUtil.hitTest(
holder.dragView!!,
val handleWidth = dragHandle.width x,
val handleHeight = dragHandle.height y
val handleLeft = dragHandle.left )
val handleTop = dragHandle.top
return (x >= handleLeft && x < handleLeft + handleWidth &&
y >= handleTop && y < handleTop + handleHeight) && position != 0
} }
override fun onMoveItem(fromPosition: Int, toPosition: Int) { override fun onMoveItem(fromPosition: Int, toPosition: Int) {
dataSet.add(toPosition - 1, dataSet.removeAt(fromPosition - 1)) dataSet.add(toPosition, dataSet.removeAt(fromPosition))
} }
override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange { override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange? {
return ItemDraggableRange(0, itemCount - 1) return null
} }
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean { override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {

View file

@ -20,7 +20,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.DimenRes import androidx.annotation.DimenRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.BundleCompat
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

View file

@ -18,11 +18,7 @@ import android.app.ActivityOptions
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.*
import android.view.MenuInflater
import android.view.MenuItem
import android.view.SubMenu
import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
@ -47,11 +43,7 @@ import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.databinding.FragmentAlbumDetailsBinding import code.name.monkey.retromusic.databinding.FragmentAlbumDetailsBinding
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.applyOutlineColor
import code.name.monkey.retromusic.extensions.findActivityNavController
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions
@ -69,11 +61,7 @@ import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.Result
import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.network.model.LastFmAlbum
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.*
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.logD
import code.name.monkey.retromusic.util.logE
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialArcMotion import com.google.android.material.transition.MaterialArcMotion

View file

@ -1,7 +1,5 @@
package code.name.monkey.retromusic.fragments.artists package code.name.monkey.retromusic.fragments.artists
import android.app.Activity
import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.Spanned import android.text.Spanned
@ -9,6 +7,7 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
@ -27,11 +26,7 @@ import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.databinding.FragmentArtistDetailsBinding import code.name.monkey.retromusic.databinding.FragmentArtistDetailsBinding
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.applyOutlineColor
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroGlideExtension.artistImageOptions import code.name.monkey.retromusic.glide.RetroGlideExtension.artistImageOptions
@ -44,12 +39,7 @@ import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.Result
import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.network.model.LastFmArtist
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.*
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.logD
import code.name.monkey.retromusic.util.logE
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialContainerTransform import com.google.android.material.transition.MaterialContainerTransform
@ -57,7 +47,7 @@ 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.get import org.koin.android.ext.android.get
import java.util.Locale import java.util.*
abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details), abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details),
IAlbumClickListener { IAlbumClickListener {
@ -153,14 +143,10 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs)) MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs))
) )
val songText = resources.getQuantityString( val songText = resources.getQuantityString(
R.plurals.albumSongs, R.plurals.albumSongs, artist.songCount, artist.songCount
artist.songCount,
artist.songCount
) )
val albumText = resources.getQuantityString( val albumText = resources.getQuantityString(
R.plurals.albums, R.plurals.albums, artist.songCount, artist.songCount
artist.songCount,
artist.songCount
) )
binding.fragmentArtistContent.songTitle.text = songText binding.fragmentArtistContent.songTitle.text = songText
binding.fragmentArtistContent.albumTitle.text = albumText binding.fragmentArtistContent.albumTitle.text = albumText
@ -174,8 +160,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
) { ) {
biography = null biography = null
this.lang = lang this.lang = lang
detailsViewModel.getArtistInfo(name, lang, null) detailsViewModel.getArtistInfo(name, lang, null).observe(viewLifecycleOwner) { result ->
.observe(viewLifecycleOwner) { result ->
when (result) { when (result) {
is Result.Loading -> logD("Loading") is Result.Loading -> logD("Loading")
is Result.Error -> logE("Error") is Result.Error -> logE("Error")
@ -214,11 +199,8 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
} }
private fun loadArtistImage(artist: Artist) { private fun loadArtistImage(artist: Artist) {
Glide.with(requireContext()) Glide.with(requireContext()).asBitmapPalette().artistImageOptions(artist)
.asBitmapPalette() .load(RetroGlideExtension.getArtistModel(artist)).dontAnimate()
.artistImageOptions(artist)
.load(RetroGlideExtension.getArtistModel(artist))
.dontAnimate()
.into(object : SingleColorTarget(binding.image) { .into(object : SingleColorTarget(binding.image) {
override fun onColorReady(color: Int) { override fun onColorReady(color: Int) {
setColors(color) setColors(color)
@ -274,13 +256,8 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
} }
R.id.action_set_artist_image -> { R.id.action_set_artist_image -> {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
selectImageLauncher.launch( selectImageLauncher.launch(
Intent.createChooser( PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
intent,
getString(R.string.pick_from_local_storage)
)
) )
return true return true
} }
@ -336,14 +313,14 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
SortOrder.ArtistSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc).isChecked = SortOrder.ArtistSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc).isChecked =
true true
SortOrder.ArtistSongSortOrder.SONG_ALBUM -> SortOrder.ArtistSongSortOrder.SONG_ALBUM -> sortOrder.findItem(R.id.action_sort_order_album).isChecked =
sortOrder.findItem(R.id.action_sort_order_album).isChecked = true true
SortOrder.ArtistSongSortOrder.SONG_YEAR -> SortOrder.ArtistSongSortOrder.SONG_YEAR -> sortOrder.findItem(R.id.action_sort_order_year).isChecked =
sortOrder.findItem(R.id.action_sort_order_year).isChecked = true true
SortOrder.ArtistSongSortOrder.SONG_DURATION -> SortOrder.ArtistSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_song_duration).isChecked =
sortOrder.findItem(R.id.action_sort_order_song_duration).isChecked = true true
else -> { else -> {
throw IllegalArgumentException("invalid $savedSongSortOrder") throw IllegalArgumentException("invalid $savedSongSortOrder")
@ -352,14 +329,11 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
} }
private val selectImageLauncher = private val selectImageLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (result.resultCode == Activity.RESULT_OK) { lifecycleScope.launch {
result.data?.data?.let { if (uri != null) {
lifecycleScope.launch { CustomArtistImageUtil.getInstance(requireContext())
CustomArtistImageUtil.getInstance(requireContext()) .setCustomArtistImage(artist, uri)
.setCustomArtistImage(artist, it)
}
} }
} }
} }

View file

@ -10,7 +10,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.ActivityRestoreBinding import code.name.monkey.retromusic.databinding.ActivityRestoreBinding
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.accentOutlineColor import code.name.monkey.retromusic.extensions.accentOutlineColor

View file

@ -248,8 +248,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme
abstract fun onHide() abstract fun onHide()
abstract fun onBackPressed(): Boolean
abstract fun toolbarIconColor(): Int abstract fun toolbarIconColor(): Int
override fun onServiceConnected() { override fun onServiceConnected() {

View file

@ -17,11 +17,7 @@ import android.content.Context
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.view.Menu import android.view.*
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
@ -77,8 +73,7 @@ import java.io.File
import java.io.FileFilter import java.io.FileFilter
import java.io.IOException import java.io.IOException
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.Collections import java.util.*
import java.util.LinkedList
class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder), class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder),
IMainActivityFragmentCallbacks, SelectionCallback, ICallbacks, IMainActivityFragmentCallbacks, SelectionCallback, ICallbacks,

View file

@ -15,12 +15,8 @@
package code.name.monkey.retromusic.fragments.home package code.name.monkey.retromusic.fragments.home
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.*
import android.view.MenuInflater
import android.view.MenuItem
import android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM import android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM
import android.view.View
import android.view.ViewGroup
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
@ -34,18 +30,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.common.ATHToolbarActivity import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.EXTRA_PLAYLIST_TYPE import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.HISTORY_PLAYLIST
import code.name.monkey.retromusic.LAST_ADDED_PLAYLIST
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.TOP_PLAYED_PLAYLIST
import code.name.monkey.retromusic.adapter.HomeAdapter import code.name.monkey.retromusic.adapter.HomeAdapter
import code.name.monkey.retromusic.databinding.FragmentHomeBinding import code.name.monkey.retromusic.databinding.FragmentHomeBinding
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.dip import code.name.monkey.retromusic.extensions.dip
import code.name.monkey.retromusic.extensions.drawNextToNavbar
import code.name.monkey.retromusic.extensions.elevatedAccentColor import code.name.monkey.retromusic.extensions.elevatedAccentColor
import code.name.monkey.retromusic.extensions.setUpMediaRouteButton import code.name.monkey.retromusic.extensions.setUpMediaRouteButton
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
@ -60,7 +51,6 @@ import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.PreferenceUtil.userName import code.name.monkey.retromusic.util.PreferenceUtil.userName
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialFadeThrough import com.google.android.material.transition.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
@ -175,7 +165,7 @@ class HomeFragment :
findNavController().navigate(R.id.action_search, null, navOptions) findNavController().navigate(R.id.action_search, null, navOptions)
} }
val hexColor = String.format("#%06X", 0xFFFFFF and accentColor()) val hexColor = String.format("#%06X", 0xFFFFFF and accentColor())
val appName = "<font color=$hexColor>Retro Music</font>".parseAsHtml() val appName = "Retro <font color=$hexColor>Music</font>".parseAsHtml()
binding.appBarLayout.title = appName binding.appBarLayout.title = appName
} }

View file

@ -20,12 +20,7 @@ import androidx.appcompat.widget.Toolbar
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.databinding.FragmentAdaptivePlayerBinding import code.name.monkey.retromusic.databinding.FragmentAdaptivePlayerBinding
import code.name.monkey.retromusic.extensions.colorControlNormal import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.extensions.textColorPrimary
import code.name.monkey.retromusic.extensions.textColorSecondary
import code.name.monkey.retromusic.extensions.whichFragment
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.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -118,11 +113,6 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) {
} }
override fun onHide() { override fun onHide() {
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -106,10 +106,6 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
override fun onHide() { override fun onHide() {
} }
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {
return Color.WHITE return Color.WHITE
} }

View file

@ -49,11 +49,6 @@ class CardFragment : AbsPlayerFragment(R.layout.fragment_card_player) {
override fun onHide() { override fun onHide() {
playbackControlsFragment.hide() playbackControlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {

View file

@ -62,11 +62,6 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
override fun onHide() { override fun onHide() {
playbackControlsFragment.hide() playbackControlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {

View file

@ -195,8 +195,6 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
override fun onHide() { override fun onHide() {
} }
override fun onBackPressed(): Boolean = false
override fun toolbarIconColor(): Int = override fun toolbarIconColor(): Int =
colorControlNormal() colorControlNormal()

View file

@ -23,6 +23,7 @@ import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import android.widget.SeekBar import android.widget.SeekBar
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.commit import androidx.fragment.app.commit
@ -163,6 +164,13 @@ class ClassicPlayerFragment : AbsPlayerFragment(R.layout.fragment_classic_player
binding.text.setOnClickListener { binding.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
requireActivity().onBackPressedDispatcher.addCallback(object: OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED) {
getQueuePanel().state = BottomSheetBehavior.STATE_COLLAPSED
}
}
})
} }
private fun hideVolumeIfAvailable() { private fun hideVolumeIfAvailable() {
@ -257,16 +265,6 @@ class ClassicPlayerFragment : AbsPlayerFragment(R.layout.fragment_classic_player
override fun onHide() { override fun onHide() {
} }
override fun onBackPressed(): Boolean {
var wasExpanded = false
if (getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED) {
wasExpanded = getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED
getQueuePanel().state = BottomSheetBehavior.STATE_COLLAPSED
return wasExpanded
}
return wasExpanded
}
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {
return Color.WHITE return Color.WHITE
} }

View file

@ -79,11 +79,6 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
override fun onHide() { override fun onHide() {
playbackControlsFragment.hide() playbackControlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {

View file

@ -49,11 +49,6 @@ class FitFragment : AbsPlayerFragment(R.layout.fragment_fit) {
override fun onHide() { override fun onHide() {
playbackControlsFragment.hide() playbackControlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {

View file

@ -100,11 +100,6 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) {
override fun onHide() { override fun onHide() {
controlsFragment.hide() controlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {

View file

@ -32,11 +32,7 @@ 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.toSongEntity import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.getTintedDrawable
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment

View file

@ -86,10 +86,6 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) {
override fun onHide() { override fun onHide() {
} }
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {
return Color.WHITE return Color.WHITE
} }

View file

@ -21,6 +21,7 @@ import android.graphics.PorterDuff
import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.AnimatedVectorDrawable
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
@ -94,16 +95,19 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
} }
override fun onStateChanged(bottomSheet: View, newState: Int) { override fun onStateChanged(bottomSheet: View, newState: Int) {
onBackPressedCallback.isEnabled = newState == STATE_EXPANDED
when (newState) { when (newState) {
STATE_EXPANDED, STATE_EXPANDED,
STATE_DRAGGING, STATE_DRAGGING,
-> { -> {
mainActivity.getBottomSheetBehavior().isDraggable = false mainActivity.getBottomSheetBehavior().isDraggable = false
} }
STATE_COLLAPSED -> { STATE_COLLAPSED -> {
resetToCurrentPosition() resetToCurrentPosition()
mainActivity.getBottomSheetBehavior().isDraggable = true mainActivity.getBottomSheetBehavior().isDraggable = true
} }
else -> { else -> {
mainActivity.getBottomSheetBehavior().isDraggable = true mainActivity.getBottomSheetBehavior().isDraggable = true
} }
@ -111,6 +115,17 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
} }
} }
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (getQueuePanel().state == STATE_EXPANDED) {
getQueuePanel().state = STATE_COLLAPSED
} else {
isEnabled = false
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
}
private fun setupFavourite() { private fun setupFavourite() {
binding.playbackControlsFragment.songFavourite.setOnClickListener { binding.playbackControlsFragment.songFavourite.setOnClickListener {
toggleFavorite(MusicPlayerRemote.currentSong) toggleFavorite(MusicPlayerRemote.currentSong)
@ -164,6 +179,10 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
insets insets
} }
binding.playbackControlsFragment.root.drawAboveSystemBars() binding.playbackControlsFragment.root.drawAboveSystemBars()
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
onBackPressedCallback
)
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@ -201,16 +220,6 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onHide() { override fun onHide() {
} }
override fun onBackPressed(): Boolean {
var wasExpanded = false
if (getQueuePanel().state == STATE_EXPANDED) {
wasExpanded = getQueuePanel().state == STATE_EXPANDED
getQueuePanel().state = STATE_COLLAPSED
return wasExpanded
}
return wasExpanded
}
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {
return Color.WHITE return Color.WHITE
} }
@ -417,6 +426,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
lastPlaybackControlsColor, lastPlaybackControlsColor,
PorterDuff.Mode.SRC_IN PorterDuff.Mode.SRC_IN
) )
else -> binding.shuffleButton.setColorFilter( else -> binding.shuffleButton.setColorFilter(
lastDisabledPlaybackControlsColor, lastDisabledPlaybackControlsColor,
PorterDuff.Mode.SRC_IN PorterDuff.Mode.SRC_IN
@ -437,6 +447,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
PorterDuff.Mode.SRC_IN PorterDuff.Mode.SRC_IN
) )
} }
MusicService.REPEAT_MODE_ALL -> { MusicService.REPEAT_MODE_ALL -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat) binding.repeatButton.setImageResource(R.drawable.ic_repeat)
binding.repeatButton.setColorFilter( binding.repeatButton.setColorFilter(
@ -444,6 +455,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
PorterDuff.Mode.SRC_IN PorterDuff.Mode.SRC_IN
) )
} }
MusicService.REPEAT_MODE_THIS -> { MusicService.REPEAT_MODE_THIS -> {
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one) binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
binding.repeatButton.setColorFilter( binding.repeatButton.setColorFilter(

View file

@ -84,10 +84,6 @@ class HomePlayerFragment : AbsPlayerFragment(R.layout.fragment_home_player),
binding.text.text = song.artistName binding.text.text = song.artistName
} }
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {
return Color.WHITE return Color.WHITE
} }

View file

@ -90,11 +90,6 @@ class MaterialFragment : AbsPlayerFragment(R.layout.fragment_material) {
override fun onHide() { override fun onHide() {
playbackControlsFragment.hide() playbackControlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor() = colorControlNormal() override fun toolbarIconColor() = colorControlNormal()

View file

@ -45,11 +45,6 @@ class MD3PlayerFragment : AbsPlayerFragment(R.layout.fragment_md3_player) {
override fun onHide() { override fun onHide() {
controlsFragment.hide() controlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {

View file

@ -27,11 +27,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.SNOWFALL import code.name.monkey.retromusic.SNOWFALL
import code.name.monkey.retromusic.databinding.FragmentPlayerBinding import code.name.monkey.retromusic.databinding.FragmentPlayerBinding
import code.name.monkey.retromusic.extensions.colorControlNormal import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.extensions.isColorLight
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.extensions.whichFragment
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.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -86,11 +82,6 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player),
override fun onHide() { override fun onHide() {
controlsFragment.hide() controlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor() = colorControlNormal() override fun toolbarIconColor() = colorControlNormal()

View file

@ -20,12 +20,7 @@ import androidx.appcompat.widget.Toolbar
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.databinding.FragmentPeekPlayerBinding import code.name.monkey.retromusic.databinding.FragmentPeekPlayerBinding
import code.name.monkey.retromusic.extensions.colorControlNormal import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.drawAboveSystemBarsWithPadding
import code.name.monkey.retromusic.extensions.getSongInfo
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.whichFragment
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
@ -93,10 +88,6 @@ class PeekPlayerFragment : AbsPlayerFragment(R.layout.fragment_peek_player) {
override fun onHide() { override fun onHide() {
} }
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor() = colorControlNormal() override fun toolbarIconColor() = colorControlNormal()
override val paletteColor: Int override val paletteColor: Int

View file

@ -102,11 +102,6 @@ class PlainPlayerFragment : AbsPlayerFragment(R.layout.fragment_plain_player) {
override fun onHide() { override fun onHide() {
plainPlaybackControlsFragment.hide() plainPlaybackControlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
} }
override fun toolbarIconColor() = colorControlNormal() override fun toolbarIconColor() = colorControlNormal()

View file

@ -71,10 +71,6 @@ class SimplePlayerFragment : AbsPlayerFragment(R.layout.fragment_simple_player)
controlsFragment.hide() controlsFragment.hide()
} }
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor() = colorControlNormal() override fun toolbarIconColor() = colorControlNormal()
override fun onColorChanged(color: MediaNotificationProcessor) { override fun onColorChanged(color: MediaNotificationProcessor) {

View file

@ -62,10 +62,6 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player),
override fun onHide() {} override fun onHide() {}
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor(): Int { override fun toolbarIconColor(): Int {
return toolbarColor return toolbarColor
} }

View file

@ -34,7 +34,6 @@ import com.google.android.material.transition.MaterialArcMotion
import com.google.android.material.transition.MaterialContainerTransform import com.google.android.material.transition.MaterialContainerTransform
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
@ -126,15 +125,11 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
val wrappedAdapter: RecyclerView.Adapter<*> = val wrappedAdapter: RecyclerView.Adapter<*> =
dragDropManager.createWrappedAdapter(playlistSongAdapter) dragDropManager.createWrappedAdapter(playlistSongAdapter)
val animator: GeneralItemAnimator = DraggableItemAnimator()
binding.recyclerView.itemAnimator = animator
dragDropManager.attachRecyclerView(binding.recyclerView)
binding.recyclerView.apply { binding.recyclerView.apply {
adapter = wrappedAdapter
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = wrappedAdapter itemAnimator = DraggableItemAnimator()
dragDropManager.attachRecyclerView(this)
ThemedFastScroller.create(this) ThemedFastScroller.create(this)
} }
playlistSongAdapter.registerAdapterDataObserver(object : playlistSongAdapter.registerAdapterDataObserver(object :

View file

@ -18,7 +18,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
class PlaylistDetailsViewModel( class PlaylistDetailsViewModel(

View file

@ -20,7 +20,6 @@ import androidx.core.os.bundleOf
import androidx.core.view.MenuCompat import androidx.core.view.MenuCompat
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import code.name.monkey.retromusic.EXTRA_PLAYLIST
import code.name.monkey.retromusic.EXTRA_PLAYLIST_ID import code.name.monkey.retromusic.EXTRA_PLAYLIST_ID
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter

View file

@ -80,9 +80,6 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
recyclerViewDragDropManager = RecyclerViewDragDropManager() recyclerViewDragDropManager = RecyclerViewDragDropManager()
recyclerViewSwipeManager = RecyclerViewSwipeManager() recyclerViewSwipeManager = RecyclerViewSwipeManager()
val animator = DraggableItemAnimator()
animator.supportsChangeAnimations = false
playingQueueAdapter = PlayingQueueAdapter( playingQueueAdapter = PlayingQueueAdapter(
requireActivity(), requireActivity(),
MusicPlayerRemote.playingQueue.toMutableList(), MusicPlayerRemote.playingQueue.toMutableList(),
@ -94,12 +91,15 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
linearLayoutManager = LinearLayoutManager(requireContext()) linearLayoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.layoutManager = linearLayoutManager
binding.recyclerView.adapter = wrappedAdapter binding.recyclerView.apply {
binding.recyclerView.itemAnimator = animator layoutManager = linearLayoutManager
recyclerViewTouchActionGuardManager?.attachRecyclerView(binding.recyclerView) adapter = wrappedAdapter
recyclerViewDragDropManager?.attachRecyclerView(binding.recyclerView) itemAnimator = DraggableItemAnimator()
recyclerViewSwipeManager?.attachRecyclerView(binding.recyclerView) recyclerViewTouchActionGuardManager?.attachRecyclerView(this)
recyclerViewDragDropManager?.attachRecyclerView(this)
recyclerViewSwipeManager?.attachRecyclerView(this)
}
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
@ -184,6 +184,7 @@ class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_q
private fun setupToolbar() { private fun setupToolbar() {
binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime() binding.appBarLayout.toolbar.subtitle = getUpNextAndQueueTime()
binding.appBarLayout.toolbar.isTitleCentered = false
binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor()) binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
ColorStateList.valueOf( ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor( MaterialValueHelper.getPrimaryTextColor(

View file

@ -49,11 +49,16 @@ class AudioSettings : AbsSettingsFragment() {
if (VersionUtils.hasS()) { if (VersionUtils.hasS()) {
bluetoothPreference?.setOnPreferenceChangeListener { _, newValue -> bluetoothPreference?.setOnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean) { if (newValue as Boolean) {
if (ActivityCompat.checkSelfPermission(requireContext(), if (ActivityCompat.checkSelfPermission(
BLUETOOTH_CONNECT) != PERMISSION_GRANTED requireContext(),
BLUETOOTH_CONNECT
) != PERMISSION_GRANTED
) { ) {
ActivityCompat.requestPermissions(requireActivity(), arrayOf( ActivityCompat.requestPermissions(
BLUETOOTH_CONNECT), BLUETOOTH_PERMISSION_REQUEST) requireActivity(), arrayOf(
BLUETOOTH_CONNECT
), BLUETOOTH_PERMISSION_REQUEST
)
} }
} }
return@setOnPreferenceChangeListener true return@setOnPreferenceChangeListener true

View file

@ -16,6 +16,8 @@ package code.name.monkey.retromusic.fragments.settings
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.preference.Preference import androidx.preference.Preference
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference
import code.name.monkey.retromusic.LANGUAGE_NAME import code.name.monkey.retromusic.LANGUAGE_NAME
@ -24,6 +26,7 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.installLanguageAndRecreate import code.name.monkey.retromusic.extensions.installLanguageAndRecreate
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType.HomeSections import code.name.monkey.retromusic.fragments.ReloadType.HomeSections
import code.name.monkey.retromusic.util.PreferenceUtil
import org.koin.androidx.viewmodel.ext.android.activityViewModel import org.koin.androidx.viewmodel.ext.android.activityViewModel
/** /**
@ -42,6 +45,8 @@ class OtherSettingsFragment : AbsSettingsFragment() {
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
PreferenceUtil.languageCode =
AppCompatDelegate.getApplicationLocales().toLanguageTags().ifEmpty { "auto" }
addPreferencesFromResource(R.xml.pref_advanced) addPreferencesFromResource(R.xml.pref_advanced)
} }
@ -56,7 +61,18 @@ class OtherSettingsFragment : AbsSettingsFragment() {
val languagePreference: Preference? = findPreference(LANGUAGE_NAME) val languagePreference: Preference? = findPreference(LANGUAGE_NAME)
languagePreference?.setOnPreferenceChangeListener { prefs, newValue -> languagePreference?.setOnPreferenceChangeListener { prefs, newValue ->
setSummary(prefs, newValue) setSummary(prefs, newValue)
requireActivity().installLanguageAndRecreate(newValue.toString()) if (newValue as? String == "auto") {
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
} else {
// Install the languages from Play Store first and then set the application locale
requireActivity().installLanguageAndRecreate(newValue.toString()) {
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.forLanguageTags(
newValue as? String
)
)
}
}
true true
} }
} }

View file

@ -17,22 +17,27 @@ package code.name.monkey.retromusic.fragments.settings
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference
import code.name.monkey.retromusic.APPBAR_MODE import code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
import code.name.monkey.retromusic.HOME_ALBUM_GRID_STYLE import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.HOME_ARTIST_GRID_STYLE import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.TAB_TEXT_MODE
class PersonalizeSettingsFragment : AbsSettingsFragment() { class PersonalizeSettingsFragment : AbsSettingsFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_ui) addPreferencesFromResource(R.xml.pref_ui)
// Hide Blur album art preference on Android 11+ devices as the lockscreen album art feature was removed by Google
// And if the feature is present in some Custom ROM's there is also an option to set blur so this preference is unnecessary on Android 11 and above
val blurredAlbumArt: ATESwitchPreference? = findPreference(BLURRED_ALBUM_ART)
blurredAlbumArt?.isVisible = !VersionUtils.hasR()
} }
override fun invalidateSettings() {} override fun invalidateSettings() {}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val albumArtOnLockscreen: ATESwitchPreference? = findPreference(ALBUM_ART_ON_LOCK_SCREEN)
albumArtOnLockscreen?.isVisible = !VersionUtils.hasT()
val homeArtistStyle: ATEListPreference? = findPreference(HOME_ARTIST_GRID_STYLE) val homeArtistStyle: ATEListPreference? = findPreference(HOME_ARTIST_GRID_STYLE)
homeArtistStyle?.setOnPreferenceChangeListener { preference, newValue -> homeArtistStyle?.setOnPreferenceChangeListener { preference, newValue ->
setSummary(preference, newValue) setSummary(preference, newValue)

View file

@ -24,7 +24,6 @@ import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import code.name.monkey.retromusic.databinding.FragmentSettingsBinding import code.name.monkey.retromusic.databinding.FragmentSettingsBinding
import code.name.monkey.retromusic.extensions.applyToolbar
import code.name.monkey.retromusic.extensions.findNavController import code.name.monkey.retromusic.extensions.findNavController
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.color.ColorCallback import com.afollestad.materialdialogs.color.ColorCallback

View file

@ -139,5 +139,7 @@ class ThemeSettingsFragment : AbsSettingsFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_general) addPreferencesFromResource(R.xml.pref_general)
val wallpaperAccent: ATESwitchPreference? = findPreference(WALLPAPER_ACCENT)
wallpaperAccent?.isVisible = VersionUtils.hasOreoMR1() && !VersionUtils.hasS()
} }
} }

View file

@ -33,7 +33,11 @@ import java.io.InputStream
@GlideModule @GlideModule
class RetroMusicGlideModule : AppGlideModule() { class RetroMusicGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) { override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
registry.prepend(PlaylistPreview::class.java, Bitmap::class.java, PlaylistPreviewLoader.Factory(context)) registry.prepend(
PlaylistPreview::class.java,
Bitmap::class.java,
PlaylistPreviewLoader.Factory(context)
)
registry.prepend( registry.prepend(
AudioFileCover::class.java, AudioFileCover::class.java,
InputStream::class.java, InputStream::class.java,

View file

@ -17,7 +17,6 @@ package code.name.monkey.retromusic.glide
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.widget.ImageView import android.widget.ImageView
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.util.ColorUtil import code.name.monkey.retromusic.util.ColorUtil

View file

@ -2,21 +2,27 @@ package code.name.monkey.retromusic.service
import android.animation.Animator import android.animation.Animator
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.content.Context
import android.media.MediaPlayer import android.media.MediaPlayer
import android.provider.Settings
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import code.name.monkey.retromusic.service.playback.Playback import code.name.monkey.retromusic.service.playback.Playback
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
class AudioFader { class AudioFader {
companion object { companion object {
fun createFadeAnimator( fun createFadeAnimator(
context: Context,
fadeInMp: MediaPlayer, fadeInMp: MediaPlayer,
fadeOutMp: MediaPlayer, fadeOutMp: MediaPlayer,
endAction: (animator: Animator) -> Unit, /* Code to run when Animator Ends*/ endAction: (animator: Animator) -> Unit, /* Code to run when Animator Ends*/
): Animator? { ): Animator? {
val duration = PreferenceUtil.crossFadeDuration * 1000 // Get Global animator scale
if (duration == 0) { val animScale = Settings.Global.getFloat(context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
// Set duration according to the global animation scale, so cross-fade actually lasts for the duration set by the user
val duration = (PreferenceUtil.crossFadeDuration * 1000 ) / animScale
if (duration == 0F) {
return null return null
} }
return ValueAnimator.ofFloat(0f, 1f).apply { return ValueAnimator.ofFloat(0f, 1f).apply {

View file

@ -34,6 +34,7 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
private var durationListener = DurationListener() private var durationListener = DurationListener()
private var mIsInitialized = false private var mIsInitialized = false
private var hasDataSource: Boolean = false /* Whether first player has DataSource */ private var hasDataSource: Boolean = false /* Whether first player has DataSource */
private var nextDataSource:String? = null
private var crossFadeAnimator: Animator? = null private var crossFadeAnimator: Animator? = null
override var callbacks: PlaybackCallbacks? = null override var callbacks: PlaybackCallbacks? = null
private var crossFadeDuration = PreferenceUtil.crossFadeDuration private var crossFadeDuration = PreferenceUtil.crossFadeDuration
@ -92,8 +93,10 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
return true return true
} }
override fun seek(whereto: Int): Int { override fun seek(whereto: Int, force: Boolean): Int {
endFade() if (force) {
endFade()
}
getNextPlayer()?.stop() getNextPlayer()?.stop()
return try { return try {
getCurrentPlayer()?.seekTo(whereto) getCurrentPlayer()?.seekTo(whereto)
@ -143,7 +146,12 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
} }
} }
override fun setNextDataSource(path: String?) {} override fun setNextDataSource(path: String?) {
// Store the next song path in nextDataSource, we'll need this just in case
// if the user closes the app, then we can't get the nextSong from musicService
// As MusicPlayerRemote won't have access to the musicService
nextDataSource = path
}
override fun setAudioSessionId(sessionId: Int): Boolean { override fun setAudioSessionId(sessionId: Int): Boolean {
return try { return try {
@ -228,7 +236,7 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
private fun crossFade(fadeInMp: MediaPlayer, fadeOutMp: MediaPlayer) { private fun crossFade(fadeInMp: MediaPlayer, fadeOutMp: MediaPlayer) {
isCrossFading = true isCrossFading = true
crossFadeAnimator = createFadeAnimator(fadeInMp, fadeOutMp) { crossFadeAnimator = createFadeAnimator(context, fadeInMp, fadeOutMp) {
crossFadeAnimator = null crossFadeAnimator = null
durationListener.start() durationListener.start()
isCrossFading = false isCrossFading = false
@ -281,7 +289,7 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
fun start() { fun start() {
job?.cancel() job?.cancel()
job = launch { job = launch {
while (true) { while (isActive) {
delay(250) delay(250)
onDurationUpdated(position(), duration()) onDurationUpdated(position(), duration())
} }
@ -298,10 +306,21 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
getNextPlayer()?.let { player -> getNextPlayer()?.let { player ->
val nextSong = MusicPlayerRemote.nextSong val nextSong = MusicPlayerRemote.nextSong
// Switch to other player (Crossfade) only if next song exists // Switch to other player (Crossfade) only if next song exists
if (nextSong != null) { // If we get an empty song it's can be because the app was cleared from background
// And MusicPlayerRemote don't have access to MusicService
if (nextSong != null && nextSong != Song.emptySong) {
nextDataSource = null
setDataSourceImpl(player, nextSong.uri.toString()) { success -> setDataSourceImpl(player, nextSong.uri.toString()) { success ->
if (success) switchPlayer() if (success) switchPlayer()
} }
}
// So we have to use the previously stored nextDataSource value
else if (!nextDataSource.isNullOrEmpty()) {
setDataSourceImpl(player, nextDataSource!!) { success ->
if (success) switchPlayer()
nextDataSource = null
}
} }
} }
} }
@ -335,7 +354,7 @@ class CrossFadePlayer(context: Context) : LocalPlayback(context) {
} }
} }
internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatchers.Main) internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatchers.Default)
fun MediaPlayer.setPlaybackSpeedPitch(speed: Float, pitch: Float) { fun MediaPlayer.setPlaybackSpeedPitch(speed: Float, pitch: Float) {
if (hasMarshmallow()) { if (hasMarshmallow()) {

View file

@ -200,7 +200,7 @@ class MultiPlayer(context: Context) : LocalPlayback(context) {
* @param whereto The offset in milliseconds from the start to seek to * @param whereto The offset in milliseconds from the start to seek to
* @return The offset in milliseconds from the start to seek to * @return The offset in milliseconds from the start to seek to
*/ */
override fun seek(whereto: Int): Int { override fun seek(whereto: Int, force: Boolean): Int {
return try { return try {
mCurrentMediaPlayer.seekTo(whereto) mCurrentMediaPlayer.seekTo(whereto)
whereto whereto

View file

@ -38,10 +38,10 @@ import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.os.BundleCompat import androidx.core.os.BundleCompat
import androidx.core.os.ParcelCompat
import androidx.media.MediaBrowserServiceCompat import androidx.media.MediaBrowserServiceCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
@ -50,12 +50,10 @@ import code.name.monkey.retromusic.activities.LockScreenActivity
import code.name.monkey.retromusic.appwidgets.* import code.name.monkey.retromusic.appwidgets.*
import code.name.monkey.retromusic.auto.AutoMediaIDHelper import code.name.monkey.retromusic.auto.AutoMediaIDHelper
import code.name.monkey.retromusic.auto.AutoMusicProvider import code.name.monkey.retromusic.auto.AutoMusicProvider
import code.name.monkey.retromusic.extensions.extra
import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.extensions.toMediaSessionQueue import code.name.monkey.retromusic.extensions.toMediaSessionQueue
import code.name.monkey.retromusic.extensions.uri import code.name.monkey.retromusic.extensions.uri
import code.name.monkey.retromusic.glide.BlurTransformation import code.name.monkey.retromusic.glide.BlurTransformation
import code.name.monkey.retromusic.glide.RetroGlideExtension.getDefaultTransition
import code.name.monkey.retromusic.glide.RetroGlideExtension.getSongModel import code.name.monkey.retromusic.glide.RetroGlideExtension.getSongModel
import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
import code.name.monkey.retromusic.helper.ShuffleHelper.makeShuffleList import code.name.monkey.retromusic.helper.ShuffleHelper.makeShuffleList
@ -289,8 +287,8 @@ class MusicService : MediaBrowserServiceCompat(),
setupMediaSession() setupMediaSession()
uiThreadHandler = Handler(Looper.getMainLooper()) uiThreadHandler = Handler(Looper.getMainLooper())
registerReceiver(widgetIntentReceiver, IntentFilter(APP_WIDGET_UPDATE)) ContextCompat.registerReceiver(this, widgetIntentReceiver, IntentFilter(APP_WIDGET_UPDATE), ContextCompat.RECEIVER_NOT_EXPORTED)
registerReceiver(updateFavoriteReceiver, IntentFilter(FAVORITE_STATE_CHANGED)) ContextCompat.registerReceiver(this, updateFavoriteReceiver, IntentFilter(FAVORITE_STATE_CHANGED), ContextCompat.RECEIVER_NOT_EXPORTED)
registerReceiver(lockScreenReceiver, IntentFilter(Intent.ACTION_SCREEN_ON)) registerReceiver(lockScreenReceiver, IntentFilter(Intent.ACTION_SCREEN_ON))
sessionToken = mediaSession?.sessionToken sessionToken = mediaSession?.sessionToken
notificationManager = getSystemService() notificationManager = getSystemService()
@ -706,7 +704,7 @@ class MusicService : MediaBrowserServiceCompat(),
|| repeatMode == REPEAT_MODE_NONE && isLastTrack || repeatMode == REPEAT_MODE_NONE && isLastTrack
) { ) {
notifyChange(PLAY_STATE_CHANGED) notifyChange(PLAY_STATE_CHANGED)
seek(0) seek(0, false)
if (pendingQuit) { if (pendingQuit) {
pendingQuit = false pendingQuit = false
quit() quit()
@ -726,7 +724,7 @@ class MusicService : MediaBrowserServiceCompat(),
if (pendingQuit || repeatMode == REPEAT_MODE_NONE && isLastTrack) { if (pendingQuit || repeatMode == REPEAT_MODE_NONE && isLastTrack) {
playbackManager.setNextDataSource(null) playbackManager.setNextDataSource(null)
pause(false) pause(false)
seek(0) seek(0, false)
if (pendingQuit) { if (pendingQuit) {
pendingQuit = false pendingQuit = false
quit() quit()
@ -973,9 +971,9 @@ class MusicService : MediaBrowserServiceCompat(),
} }
@Synchronized @Synchronized
fun seek(millis: Int): Int { fun seek(millis: Int, force: Boolean = true): Int {
return try { return try {
val newPosition = playbackManager.seek(millis) val newPosition = playbackManager.seek(millis, force)
throttledSeekHandler?.notifySeek() throttledSeekHandler?.notifySeek()
newPosition newPosition
} catch (e: Exception) { } catch (e: Exception) {
@ -1048,7 +1046,9 @@ class MusicService : MediaBrowserServiceCompat(),
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null) .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null)
.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, playingQueue.size.toLong()) .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, playingQueue.size.toLong())
if (isAlbumArtOnLockScreen) { // We must send the album art in METADATA_KEY_ALBUM_ART key on A13+ or
// else album art is blurry in notification
if (isAlbumArtOnLockScreen || VersionUtils.hasT()) {
// val screenSize: Point = RetroUtil.getScreenSize(this) // val screenSize: Point = RetroUtil.getScreenSize(this)
val request = Glide.with(this) val request = Glide.with(this)
.asBitmap() .asBitmap()

View file

@ -78,7 +78,7 @@ class PlaybackManager(val context: Context) {
} }
} }
fun seek(millis: Int): Int = playback!!.seek(millis) fun seek(millis: Int, force: Boolean): Int = playback!!.seek(millis, force)
fun setDataSource( fun setDataSource(
song: Song, song: Song,

View file

@ -45,7 +45,7 @@ interface Playback {
fun position(): Int fun position(): Int
fun seek(whereto: Int): Int fun seek(whereto: Int, force: Boolean): Int
fun setVolume(vol: Float): Boolean fun setVolume(vol: Float): Boolean

View file

@ -31,7 +31,7 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.Locale import java.util.*
class CustomArtistImageUtil private constructor(context: Context) { class CustomArtistImageUtil private constructor(context: Context) {

View file

@ -6,7 +6,6 @@ import android.content.Intent
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import code.name.monkey.retromusic.BuildConfig import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.extensions.showToast

View file

@ -98,7 +98,20 @@ object PreferenceUtil {
} }
} }
val languageCode: String get() = sharedPreferences.getString(LANGUAGE_NAME, "auto") ?: "auto" var languageCode: String
get() = sharedPreferences.getString(LANGUAGE_NAME, "auto") ?: "auto"
set(value) = sharedPreferences.edit {
putString(LANGUAGE_NAME, value)
}
var isLocaleAutoStorageEnabled: Boolean
get() = sharedPreferences.getBoolean(
LOCALE_AUTO_STORE_ENABLED,
false
)
set(value) = sharedPreferences.edit {
putBoolean(LOCALE_AUTO_STORE_ENABLED, value)
}
var Fragment.userName var Fragment.userName
get() = sharedPreferences.getString( get() = sharedPreferences.getString(
@ -266,11 +279,6 @@ object PreferenceUtil {
ALBUM_ART_ON_LOCK_SCREEN, true ALBUM_ART_ON_LOCK_SCREEN, true
) )
val isAudioDucking
get() = sharedPreferences.getBoolean(
AUDIO_DUCKING, true
)
val isBluetoothSpeaker val isBluetoothSpeaker
get() = sharedPreferences.getBoolean( get() = sharedPreferences.getBoolean(
BLUETOOTH_PLAYBACK, false BLUETOOTH_PLAYBACK, false
@ -279,7 +287,7 @@ object PreferenceUtil {
val isBlurredAlbumArt val isBlurredAlbumArt
get() = sharedPreferences.getBoolean( get() = sharedPreferences.getBoolean(
BLURRED_ALBUM_ART, false BLURRED_ALBUM_ART, false
) ) && !VersionUtils.hasR()
val blurAmount get() = sharedPreferences.getInt(NEW_BLUR_AMOUNT, 25) val blurAmount get() = sharedPreferences.getInt(NEW_BLUR_AMOUNT, 25)

View file

@ -4,7 +4,6 @@ import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.appcompat.widget.Toolbar
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import code.name.monkey.retromusic.databinding.CollapsingAppbarLayoutBinding import code.name.monkey.retromusic.databinding.CollapsingAppbarLayoutBinding
import code.name.monkey.retromusic.databinding.SimpleAppbarLayoutBinding import code.name.monkey.retromusic.databinding.SimpleAppbarLayoutBinding

View file

@ -13,17 +13,16 @@
--> -->
<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">
<code.name.monkey.retromusic.views.TopAppBarLayout <code.name.monkey.retromusic.views.TopAppBarLayout
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"
style="?appBarLayoutStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true" android:fitsSystemWindows="true" />
app:liftOnScroll="true" />
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/container" android:id="@+id/container"

View file

@ -44,18 +44,19 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
app:cardCornerRadius="6dp" app:cardCornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/drag_view" app:layout_constraintStart_toEndOf="@id/drag_view"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:gravity="center_vertical" android:gravity="center_vertical"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.Small"
tools:srcCompat="@tools:sample/backgrounds/scenic" tools:srcCompat="@tools:sample/backgrounds/scenic"
tools:visibility="visible" /> tools:visibility="visible" />
@ -93,6 +94,7 @@
android:id="@+id/text" android:id="@+id/text"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<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">

View file

@ -35,18 +35,19 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
app:cardCornerRadius="10dp" app:cardCornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/drag_view" app:layout_constraintStart_toEndOf="@id/drag_view"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="45dp" android:layout_width="45dp"
android:layout_height="45dp" android:layout_height="45dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:gravity="center_vertical" android:gravity="center_vertical"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:shapeAppearance="?shapeAppearanceCornerSmall"
tools:srcCompat="@tools:sample/backgrounds/scenic" tools:srcCompat="@tools:sample/backgrounds/scenic"
tools:visibility="visible" /> tools:visibility="visible" />
@ -87,6 +88,7 @@
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
android:layout_marginTop="4dp"
android:textAppearance="@style/TextViewBody2" android:textAppearance="@style/TextViewBody2"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View file

@ -4,14 +4,15 @@
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="wrap_content" android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:background="?rectSelector" android:background="?rectSelector"
android:transitionGroup="true"> android:clickable="true"
android:focusable="true">
<FrameLayout <FrameLayout
android:id="@+id/dummy_view" android:id="@+id/dummy_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -27,6 +28,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_gravity="center_vertical|start" android:layout_gravity="center_vertical|start"
android:paddingHorizontal="8dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -34,7 +36,6 @@
app:srcCompat="@drawable/ic_drag_handle" app:srcCompat="@drawable/ic_drag_handle"
app:tint="?attr/colorControlNormal" app:tint="?attr/colorControlNormal"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
android:paddingHorizontal="8dp"
tools:visibility="visible" /> tools:visibility="visible" />
@ -43,18 +44,20 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
app:cardCornerRadius="6dp" android:transitionGroup="true"
app:cardCornerRadius="4dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/drag_view" app:layout_constraintStart_toEndOf="@id/drag_view"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:gravity="center_vertical" android:gravity="center_vertical"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.ExtraSmall"
tools:srcCompat="@tools:sample/backgrounds/scenic" tools:srcCompat="@tools:sample/backgrounds/scenic"
tools:visibility="visible" /> tools:visibility="visible" />
@ -94,6 +97,7 @@
android:id="@+id/text" android:id="@+id/text"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="isNotOreo">false</bool>
</resources>

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="wallpaper_accent_visible">true</bool>
</resources>

View file

@ -2,6 +2,4 @@
<resources> <resources>
<bool name="md3_available">true</bool> <bool name="md3_available">true</bool>
<bool name="md3_enabled">true</bool> <bool name="md3_enabled">true</bool>
<bool name="wallpaper_accent_visible">false</bool>
</resources> </resources>

View file

@ -5,9 +5,5 @@
<bool name="allowBackup">true</bool> <bool name="allowBackup">true</bool>
<bool name="wallpaper_accent_enabled">false</bool>
<bool name="wallpaper_accent_visible">false</bool>
<bool name="isNotOreo">true</bool>
<bool name="isMarshmallow">false</bool> <bool name="isMarshmallow">false</bool>
</resources> </resources>

View file

@ -562,4 +562,5 @@
<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="could_not_write_tags_to_file">Could not write tags to the music file!</string>
</resources> </resources>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="ar-SA" />
<locale android:name="eu-ES" />
<locale android:name="my-MM" />
<locale android:name="zh-CN" />
<locale android:name="zh-HK" />
<locale android:name="zh-TW" />
<locale android:name="hr" />
<locale android:name="cs-cz" />
<locale android:name="da-DK" />
<locale android:name="nl-NL" />
<locale android:name="en" />
<locale android:name="fil-PH" />
<locale android:name="fr" />
<locale android:name="de" />
<locale android:name="el" />
<locale android:name="hi" />
<locale android:name="hu" />
<locale android:name="in" />
<locale android:name="it" />
<locale android:name="ja" />
<locale android:name="kn" />
<locale android:name="ko" />
<locale android:name="kmr-TR" />
<locale android:name="lv-LV" />
<locale android:name="ml" />
<locale android:name="fa" />
<locale android:name="pl" />
<locale android:name="pt-BR" />
<locale android:name="pt-PT" />
<locale android:name="ro" />
<locale android:name="ru" />
<locale android:name="sr" />
<locale android:name="es" />
<locale android:name="es-419" />
<locale android:name="sv" />
<locale android:name="ta" />
<locale android:name="te" />
<locale android:name="th-TH" />
<locale android:name="tr" />
<locale android:name="uk" />
<locale android:name="vi" />
</locale-config>

View file

@ -2,15 +2,6 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <androidx.preference.PreferenceScreen 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">
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
android:defaultValue="true"
android:key="audio_ducking"
android:layout="@layout/list_item_view_switch"
android:summary="@string/pref_summary_audio_ducking"
android:title="@string/pref_title_audio_ducking"
app:icon="@drawable/ic_volume_down"
app:isPreferenceVisible="@bool/isNotOreo"/>
<code.name.monkey.retromusic.preferences.DurationPreference <code.name.monkey.retromusic.preferences.DurationPreference
android:defaultValue="0" android:defaultValue="0"
android:key="audio_fade_duration" android:key="audio_fade_duration"

View file

@ -43,12 +43,11 @@
android:title="@string/colors"> android:title="@string/colors">
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference <code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
android:defaultValue="@bool/wallpaper_accent_enabled" android:defaultValue="false"
android:key="wallpaper_accent" android:key="wallpaper_accent"
android:layout="@layout/list_item_view_switch" android:layout="@layout/list_item_view_switch"
android:summary="@string/pref_summary_wallpaper_accent" android:summary="@string/pref_summary_wallpaper_accent"
android:title="@string/pref_title_wallpaper_accent" android:title="@string/pref_title_wallpaper_accent" />
app:isPreferenceVisible="@bool/wallpaper_accent_visible" />
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATEColorPreference <code.name.monkey.appthemehelper.common.prefs.supportv7.ATEColorPreference
android:dependency="material_you" android:dependency="material_you"

View file

@ -12,20 +12,22 @@ import com.google.android.play.core.splitinstall.SplitInstallManagerFactory
import com.google.android.play.core.splitinstall.SplitInstallRequest import com.google.android.play.core.splitinstall.SplitInstallRequest
import com.google.android.play.core.splitinstall.SplitInstallSessionState import com.google.android.play.core.splitinstall.SplitInstallSessionState
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus
import java.util.* import java.util.*
fun Context.setUpMediaRouteButton(menu: Menu) { fun Context.setUpMediaRouteButton(menu: Menu) {
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.action_cast) CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.action_cast)
} }
fun FragmentActivity.installLanguageAndRecreate(code: String) { fun FragmentActivity.installLanguageAndRecreate(code: String, onInstallComplete: () -> Unit) {
var mySessionId = 0 var mySessionId = 0
val manager = SplitInstallManagerFactory.create(this) val manager = SplitInstallManagerFactory.create(this)
val listener = object: SplitInstallStateUpdatedListener{ val listener = object: SplitInstallStateUpdatedListener{
override fun onStateUpdate(state: SplitInstallSessionState) { override fun onStateUpdate(state: SplitInstallSessionState) {
if (state.sessionId() == mySessionId) { // Restart the activity if the language is installed (sessionId is same and status is installed)
recreate() if (state.sessionId() == mySessionId && state.status() == SplitInstallSessionStatus.INSTALLED) {
onInstallComplete()
manager.unregisterListener(this) manager.unregisterListener(this)
} }
} }

View file

@ -79,7 +79,7 @@ class CastPlayer(castSession: CastSession) : Playback, RemoteMediaClient.Callbac
return remoteMediaClient?.approximateStreamPosition?.toInt() ?: 0 return remoteMediaClient?.approximateStreamPosition?.toInt() ?: 0
} }
override fun seek(whereto: Int): Int { override fun seek(whereto: Int, force: Boolean): Int {
remoteMediaClient?.seek(MediaSeekOptions.Builder().setPosition(whereto.toLong()).build()) remoteMediaClient?.seek(MediaSeekOptions.Builder().setPosition(whereto.toLong()).build())
return whereto return whereto
} }

View file

@ -1,12 +1,12 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
compileSdk 32 compileSdk 33
namespace "code.name.monkey.appthemehelper" namespace "code.name.monkey.appthemehelper"
defaultConfig { defaultConfig {
minSdk 21 minSdk 21
targetSdk 32 targetSdk 33
} }
buildTypes { buildTypes {
release { release {

View file

@ -82,4 +82,13 @@ object VersionUtils {
fun hasS(): Boolean { fun hasS(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
} }
/**
* @return true if device is running API >= 33
*/
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
@JvmStatic
fun hasT(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
}
} }

View file

@ -2,9 +2,9 @@
buildscript { buildscript {
ext { ext {
lifecycle_version = '2.6.0' lifecycle_version = '2.6.1'
navigation_version = '2.5.3' navigation_version = '2.5.3'
mdc_version = '1.9.0-alpha02' mdc_version = '1.9.0-beta01'
preference_version = '1.2.0' preference_version = '1.2.0'
appcompat_version = '1.6.1' appcompat_version = '1.6.1'
core_version = '1.10.0-rc01' core_version = '1.10.0-rc01'