Merge branch 'RetroMusicPlayer:dev' into fix-blacklist-perms-android-13

This commit is contained in:
sevonj 2024-12-06 22:27:31 +00:00 committed by GitHub
commit 5e4997b1b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 219 additions and 73 deletions

View file

@ -2,9 +2,9 @@ name: Android CI
on: on:
push: push:
branches: [ dev ] branches: [dev]
pull_request: pull_request:
branches: [ dev ] branches: [dev]
jobs: jobs:
check: check:
@ -16,7 +16,7 @@ jobs:
- uses: actions/setup-java@v4 - uses: actions/setup-java@v4
with: with:
distribution: 'zulu' distribution: 'zulu'
java-version: 17 java-version: 21
- uses: gradle/actions/setup-gradle@v3 - uses: gradle/actions/setup-gradle@v3
- name: Lint Android - name: Lint Android
run: ./gradlew lint run: ./gradlew lint
@ -28,7 +28,7 @@ jobs:
- uses: actions/setup-java@v4 - uses: actions/setup-java@v4
with: with:
distribution: 'zulu' distribution: 'zulu'
java-version: 17 java-version: 21
- uses: gradle/actions/setup-gradle@v3 - uses: gradle/actions/setup-gradle@v3
- name: Build - name: Build
run: ./gradlew app:assemble run: ./gradlew app:assemble

View file

@ -5,18 +5,18 @@ apply plugin: 'kotlin-parcelize'
apply plugin: 'com.google.devtools.ksp' apply plugin: 'com.google.devtools.ksp'
android { android {
compileSdk 34 compileSdk 35
namespace "code.name.monkey.retromusic" namespace "code.name.monkey.retromusic"
defaultConfig { defaultConfig {
minSdk 21 minSdk 21
targetSdk 34 targetSdk 35
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
applicationId namespace applicationId namespace
versionCode 10603 versionCode 10620
versionName '6.1.0' versionName '6.2.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')}\"")
} }
@ -69,11 +69,11 @@ android {
warning 'ImpliedQuantity', 'Instantiatable', 'MissingQuantity', 'MissingTranslation', 'StringFormatInvalid' warning 'ImpliedQuantity', 'Instantiatable', 'MissingQuantity', 'MissingTranslation', 'StringFormatInvalid'
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_17 sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_21
} }
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "21"
} }
dependenciesInfo { dependenciesInfo {
includeInApk = false includeInApk = false

View file

@ -73,4 +73,4 @@
# TypeToken https://stackoverflow.com/questions/70969756/caused-by-java-lang-runtimeexception-missing-type-parameter # TypeToken https://stackoverflow.com/questions/70969756/caused-by-java-lang-runtimeexception-missing-type-parameter
-keep class com.google.gson.reflect.TypeToken -keep class com.google.gson.reflect.TypeToken
-keep class * extends com.google.gson.reflect.TypeToken -keep class * extends com.google.gson.reflect.TypeToken
-keep public class * implements java.lang.reflect.Type -keep public class * implements java.lang.reflect.Type

View file

@ -161,4 +161,5 @@ const val CIRCLE_PLAY_BUTTON = "circle_play_button"
const val SWIPE_ANYWHERE_NOW_PLAYING = "swipe_anywhere_now_playing" const val SWIPE_ANYWHERE_NOW_PLAYING = "swipe_anywhere_now_playing"
const val PAUSE_HISTORY = "pause_history" const val PAUSE_HISTORY = "pause_history"
const val MANAGE_AUDIO_FOCUS = "manage_audio_focus" const val MANAGE_AUDIO_FOCUS = "manage_audio_focus"
const val SWIPE_DOWN_DISMISS = "swipe_to_dismiss" const val SWIPE_DOWN_DISMISS = "swipe_to_dismiss"
const val ENABLE_SEARCH_PLAYLIST= "enable_search_playlist"

View file

@ -252,7 +252,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this) PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
when (key) { when (key) {
SWIPE_DOWN_DISMISS -> { SWIPE_DOWN_DISMISS -> {
bottomSheetBehavior.isHideable = PreferenceUtil.swipeDownToDismiss bottomSheetBehavior.isHideable = PreferenceUtil.swipeDownToDismiss

View file

@ -25,6 +25,7 @@ import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.db.toSongsEntity 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.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.ViewUtil import code.name.monkey.retromusic.util.ViewUtil
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
@ -43,9 +44,20 @@ class OrderablePlaylistSongAdapter(
val libraryViewModel: LibraryViewModel by activity.viewModel() val libraryViewModel: LibraryViewModel by activity.viewModel()
private var filtered = false
private var filter: CharSequence? = null
private var fullDataSet: MutableList<Song>
init { init {
this.setHasStableIds(true) this.setHasStableIds(true)
this.setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection) this.setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection)
fullDataSet = dataSet.toMutableList()
}
override fun swapDataSet(dataSet: List<Song>) {
super.swapDataSet(dataSet)
fullDataSet = dataSet.toMutableList()
onFilter(filter)
} }
override fun getItemId(position: Int): Long { override fun getItemId(position: Int): Long {
@ -90,13 +102,22 @@ class OrderablePlaylistSongAdapter(
return super.onSongMenuItemClick(item) return super.onSongMenuItemClick(item)
} }
override fun onClick(v: View?) {
if (isInQuickSelectMode || !filtered) {
super.onClick(v)
} else {
val position = fullDataSet.indexOf(dataSet.get(layoutPosition))
MusicPlayerRemote.openQueueKeepShuffleMode(fullDataSet, position, true)
}
}
init { init {
dragView?.isVisible = true dragView?.isVisible = true
} }
} }
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 (isInQuickSelectMode) { if (isInQuickSelectMode || filtered) {
return false return false
} }
return ViewUtil.hitTest(holder.imageText!!, x, y) || ViewUtil.hitTest( return ViewUtil.hitTest(holder.imageText!!, x, y) || ViewUtil.hitTest(
@ -127,8 +148,27 @@ class OrderablePlaylistSongAdapter(
} }
fun saveSongs(playlistEntity: PlaylistEntity) { fun saveSongs(playlistEntity: PlaylistEntity) {
onFilter(null)
activity.lifecycleScope.launch(Dispatchers.IO) { activity.lifecycleScope.launch(Dispatchers.IO) {
libraryViewModel.insertSongs(dataSet.toSongsEntity(playlistEntity)) libraryViewModel.insertSongs(dataSet.toSongsEntity(playlistEntity))
} }
} }
fun onFilter(text: CharSequence?) {
filter = text
if (text.isNullOrEmpty()) {
filtered = false
dataSet = fullDataSet
} else {
filtered = true
dataSet = fullDataSet.filter { song -> song.title.contains(text, ignoreCase = true) }
.toMutableList()
}
notifyDataSetChanged()
}
fun hasSongs(): Boolean {
return itemCount > 0 || (filtered && fullDataSet.size > 0)
}
} }

View file

@ -216,7 +216,7 @@ open class SongAdapter(
if (isInQuickSelectMode) { if (isInQuickSelectMode) {
toggleChecked(layoutPosition) toggleChecked(layoutPosition)
} else { } else {
MusicPlayerRemote.openQueue(dataSet, layoutPosition, true) MusicPlayerRemote.openQueueKeepShuffleMode(dataSet, layoutPosition, true)
} }
} }

View file

@ -68,6 +68,7 @@ class AppShortcutLauncherActivity : Activity() {
INTENT_EXTRA_PLAYLIST to playlist, INTENT_EXTRA_PLAYLIST to playlist,
INTENT_EXTRA_SHUFFLE_MODE to shuffleMode INTENT_EXTRA_SHUFFLE_MODE to shuffleMode
) )
intent.setPackage(this.packageName)
intent.putExtras(bundle) intent.putExtras(bundle)

View file

@ -198,4 +198,4 @@ class SleepTimerDialog : DialogFragment() {
override fun onFinish() {} override fun onFinish() {}
} }
} }

View file

@ -374,7 +374,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme
e1: MotionEvent?, e1: MotionEvent?,
e2: MotionEvent, e2: MotionEvent,
distanceX: Float, distanceX: Float,
distanceY: Float distanceY: Float,
): Boolean { ): Boolean {
return when { return when {
abs(distanceX) > abs(distanceY) -> { abs(distanceX) > abs(distanceY) -> {

View file

@ -69,9 +69,9 @@ class CoverLyricsFragment : AbsMusicServiceFragment(R.layout.fragment_cover_lyri
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
if (key == SHOW_LYRICS) { if (key == SHOW_LYRICS) {
if (sharedPreferences?.getBoolean(key, false) == true) { if (sharedPreferences.getBoolean(key, false) == true) {
progressViewUpdateHelper?.start() progressViewUpdateHelper?.start()
binding.root.isVisible = true binding.root.isVisible = true
updateLyrics() updateLyrics()

View file

@ -152,7 +152,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player),
) )
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
if (key == SNOWFALL) { if (key == SNOWFALL) {
startOrStopSnow(PreferenceUtil.isSnowFalling) startOrStopSnow(PreferenceUtil.isSnowFalling)
} }

View file

@ -8,6 +8,8 @@ import android.view.MenuItem
import android.view.View import android.view.View
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -21,12 +23,14 @@ import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.elevatedAccentColor import code.name.monkey.retromusic.extensions.elevatedAccentColor
import code.name.monkey.retromusic.extensions.surfaceColor 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.fragments.search.clearText
import code.name.monkey.retromusic.glide.RetroGlideExtension.playlistOptions import code.name.monkey.retromusic.glide.RetroGlideExtension.playlistOptions
import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ThemedFastScroller import code.name.monkey.retromusic.util.ThemedFastScroller
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
@ -35,6 +39,9 @@ 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.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
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
@ -51,6 +58,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
private lateinit var playlist: PlaylistWithSongs private lateinit var playlist: PlaylistWithSongs
private lateinit var playlistSongAdapter: OrderablePlaylistSongAdapter private lateinit var playlistSongAdapter: OrderablePlaylistSongAdapter
private val _searchFlow = MutableSharedFlow<CharSequence?>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform(requireContext(), true).apply { sharedElementEnterTransition = MaterialContainerTransform(requireContext(), true).apply {
@ -71,6 +80,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
// binding.container.transitionName = playlist.playlistEntity.playlistName // binding.container.transitionName = playlist.playlistEntity.playlistName
setUpRecyclerView() setUpRecyclerView()
setUpSearch()
setupButtons() setupButtons()
viewModel.getPlaylist().observe(viewLifecycleOwner) { playlistWithSongs -> viewModel.getPlaylist().observe(viewLifecycleOwner) { playlistWithSongs ->
playlist = playlistWithSongs playlist = playlistWithSongs
@ -112,6 +122,33 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
} }
} }
private fun setUpSearch() {
if (!PreferenceUtil.enableSearchPlaylist) {
binding.playlistSearchView.visibility = View.GONE
} else {
binding.playlistSearchView.visibility = View.VISIBLE
}
binding.playlistSearchView.addTextChangedListener { text ->
lifecycleScope.launch {
_searchFlow.emit(text)
binding.clearSearch.visibility =
if (text.isNullOrBlank()) View.GONE else View.VISIBLE
}
}
binding.clearSearch.setOnClickListener {
lifecycleScope.launch {
_searchFlow.emit(null)
binding.playlistSearchView.clearText()
binding.clearSearch.visibility = View.GONE
}
}
lifecycleScope.launch {
_searchFlow.debounce(300).collect { text ->
playlistSongAdapter.onFilter(text)
}
}
}
private fun setUpRecyclerView() { private fun setUpRecyclerView() {
playlistSongAdapter = OrderablePlaylistSongAdapter( playlistSongAdapter = OrderablePlaylistSongAdapter(
arguments.extraPlaylistId, arguments.extraPlaylistId,
@ -150,8 +187,18 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
} }
private fun checkIsEmpty() { private fun checkIsEmpty() {
binding.empty.isVisible = playlistSongAdapter.itemCount == 0 if (_binding != null) {
binding.emptyText.isVisible = playlistSongAdapter.itemCount == 0 if (playlistSongAdapter.itemCount != 0) {
binding.empty.isVisible = false
} else {
binding.empty.isVisible = true
if (playlistSongAdapter.hasSongs()) {
binding.emptyText.text = getString(R.string.no_search_results)
} else {
binding.emptyText.text = getString(R.string.no_songs)
}
}
}
} }
override fun onDestroy() { override fun onDestroy() {
@ -160,6 +207,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
} }
override fun onPause() { override fun onPause() {
binding.playlistSearchView.clearText()
playlistSongAdapter.saveSongs(playlist.playlistEntity) playlistSongAdapter.saveSongs(playlist.playlistEntity)
super.onPause() super.onPause()
} }

View file

@ -31,7 +31,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil
class NotificationSettingsFragment : AbsSettingsFragment(), class NotificationSettingsFragment : AbsSettingsFragment(),
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
if (key == CLASSIC_NOTIFICATION) { if (key == CLASSIC_NOTIFICATION) {
if (VERSION.SDK_INT >= VERSION_CODES.O) { if (VERSION.SDK_INT >= VERSION_CODES.O) {
findPreference<Preference>(COLORED_NOTIFICATION)?.isEnabled = findPreference<Preference>(COLORED_NOTIFICATION)?.isEnabled =

View file

@ -72,7 +72,7 @@ class NowPlayingSettingsFragment : AbsSettingsFragment(),
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this) PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
when (key) { when (key) {
NOW_PLAYING_SCREEN_ID -> updateNowPlayingScreenSummary() NOW_PLAYING_SCREEN_ID -> updateNowPlayingScreenSummary()
ALBUM_COVER_STYLE -> updateAlbumCoverStyleSummary() ALBUM_COVER_STYLE -> updateAlbumCoverStyleSummary()

View file

@ -39,11 +39,14 @@ import java.io.File
object RetroGlideExtension { object RetroGlideExtension {
private const val DEFAULT_ARTIST_IMAGE = private val DEFAULT_ARTIST_IMAGE
R.drawable.default_artist_art get() = R.drawable.default_artist_art
private const val DEFAULT_SONG_IMAGE: Int = R.drawable.default_audio_art private val DEFAULT_SONG_IMAGE: Int
private const val DEFAULT_ALBUM_IMAGE = R.drawable.default_album_art get() = R.drawable.default_audio_art
private const val DEFAULT_ERROR_IMAGE_BANNER = R.drawable.material_design_default private val DEFAULT_ALBUM_IMAGE
get() = R.drawable.default_album_art
private val DEFAULT_ERROR_IMAGE_BANNER
get() = R.drawable.material_design_default
private val DEFAULT_DISK_CACHE_STRATEGY_ARTIST = DiskCacheStrategy.RESOURCE private val DEFAULT_DISK_CACHE_STRATEGY_ARTIST = DiskCacheStrategy.RESOURCE
private val DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE private val DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE

View file

@ -20,7 +20,8 @@ import code.name.monkey.retromusic.R
object HorizontalAdapterHelper { object HorizontalAdapterHelper {
const val LAYOUT_RES = R.layout.item_image val LAYOUT_RES
get() = R.layout.item_image
private const val TYPE_FIRST = 1 private const val TYPE_FIRST = 1
private const val TYPE_MIDDLE = 2 private const val TYPE_MIDDLE = 2

View file

@ -211,15 +211,7 @@ object MusicPlayerRemote : KoinComponent {
*/ */
@JvmStatic @JvmStatic
fun openQueue(queue: List<Song>, startPosition: Int, startPlaying: Boolean) { fun openQueue(queue: List<Song>, startPosition: Int, startPlaying: Boolean) {
if (!tryToHandleOpenPlayingQueue( doOpenQueue(queue, startPosition, startPlaying, MusicService.SHUFFLE_MODE_NONE)
queue,
startPosition,
startPlaying
) && musicService != null
) {
musicService?.openQueue(queue, startPosition, startPlaying)
setShuffleMode(MusicService.SHUFFLE_MODE_NONE)
}
} }
@JvmStatic @JvmStatic
@ -229,6 +221,15 @@ object MusicPlayerRemote : KoinComponent {
startPosition = Random().nextInt(queue.size) startPosition = Random().nextInt(queue.size)
} }
doOpenQueue(queue, startPosition, startPlaying, MusicService.SHUFFLE_MODE_SHUFFLE)
}
@JvmStatic
fun openQueueKeepShuffleMode(queue: List<Song>, startPosition: Int, startPlaying: Boolean) {
doOpenQueue(queue, startPosition, startPlaying, shuffleMode)
}
private fun doOpenQueue(queue: List<Song>, startPosition: Int, startPlaying: Boolean, shuffleMode: Int) {
if (!tryToHandleOpenPlayingQueue( if (!tryToHandleOpenPlayingQueue(
queue, queue,
startPosition, startPosition,
@ -236,7 +237,7 @@ object MusicPlayerRemote : KoinComponent {
) && musicService != null ) && musicService != null
) { ) {
musicService?.openQueue(queue, startPosition, startPlaying) musicService?.openQueue(queue, startPosition, startPlaying)
setShuffleMode(MusicService.SHUFFLE_MODE_SHUFFLE) setShuffleMode(shuffleMode)
} }
} }

View file

@ -48,7 +48,8 @@ import org.koin.core.component.get
import java.io.File import java.io.File
object SongMenuHelper : KoinComponent { object SongMenuHelper : KoinComponent {
const val MENU_RES = R.menu.menu_item_song val MENU_RES
get() = R.menu.menu_item_song
fun handleMenuClick(activity: FragmentActivity, song: Song, menuItemId: Int): Boolean { fun handleMenuClick(activity: FragmentActivity, song: Song, menuItemId: Int): Boolean {
val libraryViewModel = activity.getViewModel() as LibraryViewModel val libraryViewModel = activity.getViewModel() as LibraryViewModel

View file

@ -9,6 +9,7 @@ import android.media.AudioManager
import android.media.MediaPlayer import android.media.MediaPlayer
import android.media.PlaybackParams import android.media.PlaybackParams
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.media.AudioAttributesCompat import androidx.media.AudioAttributesCompat
@ -157,9 +158,11 @@ abstract class LocalPlayback(val context: Context) : Playback, MediaPlayer.OnErr
private fun registerBecomingNoisyReceiver() { private fun registerBecomingNoisyReceiver() {
if (!becomingNoisyReceiverRegistered) { if (!becomingNoisyReceiverRegistered) {
context.registerReceiver( ContextCompat.registerReceiver(
context,
becomingNoisyReceiver, becomingNoisyReceiver,
becomingNoisyReceiverIntentFilter becomingNoisyReceiverIntentFilter,
ContextCompat.RECEIVER_EXPORTED
) )
becomingNoisyReceiverRegistered = true becomingNoisyReceiverRegistered = true
} }

View file

@ -633,7 +633,9 @@ class MusicService : MediaBrowserServiceCompat(),
} }
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences, key: String?,
) {
when (key) { when (key) {
PLAYBACK_SPEED, PLAYBACK_PITCH -> { PLAYBACK_SPEED, PLAYBACK_PITCH -> {
updateMediaSessionPlaybackState() updateMediaSessionPlaybackState()
@ -1276,7 +1278,7 @@ class MusicService : MediaBrowserServiceCompat(),
private fun registerHeadsetEvents() { private fun registerHeadsetEvents() {
if (!headsetReceiverRegistered && isHeadsetPlugged) { if (!headsetReceiverRegistered && isHeadsetPlugged) {
registerReceiver(headsetReceiver, headsetReceiverIntentFilter) ContextCompat.registerReceiver(this, headsetReceiver, headsetReceiverIntentFilter, ContextCompat.RECEIVER_EXPORTED)
headsetReceiverRegistered = true headsetReceiverRegistered = true
} }
} }

View file

@ -167,14 +167,14 @@ class PackageValidator(
private fun buildCallerInfo(callingPackage: String): CallerPackageInfo? { private fun buildCallerInfo(callingPackage: String): CallerPackageInfo? {
val packageInfo = getPackageInfo(callingPackage) ?: return null val packageInfo = getPackageInfo(callingPackage) ?: return null
val appName = packageInfo.applicationInfo.loadLabel(packageManager).toString() val appName = packageInfo.applicationInfo?.loadLabel(packageManager)?.toString() ?: return null
val uid = packageInfo.applicationInfo.uid val uid = packageInfo.applicationInfo?.uid ?: return null
val signature = getSignature(packageInfo) val signature = getSignature(packageInfo)
val requestedPermissions = packageInfo.requestedPermissions val requestedPermissions = packageInfo.requestedPermissions ?: emptyArray()
val permissionFlags = packageInfo.requestedPermissionsFlags val permissionFlags = packageInfo.requestedPermissionsFlags ?: IntArray(requestedPermissions.size)
val activePermissions = mutableSetOf<String>() val activePermissions = mutableSetOf<String>()
requestedPermissions?.forEachIndexed { index, permission -> requestedPermissions.forEachIndexed { index, permission ->
if (permissionFlags[index] and REQUESTED_PERMISSION_GRANTED != 0) { if (permissionFlags[index] and REQUESTED_PERMISSION_GRANTED != 0) {
activePermissions += permission activePermissions += permission
} }
@ -207,12 +207,13 @@ class PackageValidator(
*/ */
@Suppress("deprecation") @Suppress("deprecation")
private fun getSignature(packageInfo: PackageInfo): String? { private fun getSignature(packageInfo: PackageInfo): String? {
// Security best practices dictate that an app should be signed with exactly one (1) val signatures = packageInfo.signatures
// signature. Because of this, if there are multiple signatures, reject it. return if (signatures == null || signatures.size != 1) {
return if (packageInfo.signatures == null || packageInfo.signatures.size != 1) { // Security best practices dictate that an app should be signed with exactly one (1)
// signature. Because of this, if there are multiple signatures, reject it.
null null
} else { } else {
val certificate = packageInfo.signatures[0].toByteArray() val certificate = signatures[0].toByteArray()
getSignatureSha256(certificate) getSignatureSha256(certificate)
} }
} }

View file

@ -680,6 +680,9 @@ object PreferenceUtil {
val rememberLastTab: Boolean val rememberLastTab: Boolean
get() = sharedPreferences.getBoolean(REMEMBER_LAST_TAB, true) get() = sharedPreferences.getBoolean(REMEMBER_LAST_TAB, true)
val enableSearchPlaylist: Boolean
get() = sharedPreferences.getBoolean(ENABLE_SEARCH_PLAYLIST, true)
var lastTab: Int var lastTab: Int
get() = sharedPreferences get() = sharedPreferences
.getInt(LAST_USED_TAB, 0) .getInt(LAST_USED_TAB, 0)

View file

@ -30,6 +30,33 @@
android:paddingBottom="16dp" android:paddingBottom="16dp"
android:paddingHorizontal="16dp"> android:paddingHorizontal="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/playlistSearchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/play_button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/clear_search"
android:background="@null"
android:hint="@string/action_search"
android:inputType="text|textAutoComplete"
android:padding="12dp"
android:textAppearance="@style/TextViewSubtitle1">
</com.google.android.material.textfield.TextInputEditText>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/clear_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?roundSelector"
android:padding="10dp"
android:visibility="gone"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/playlistSearchView"
app:srcCompat="@drawable/ic_close"
app:tint="?attr/colorControlNormal"
tools:visibility="visible" />
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="0dp" android:layout_width="0dp"
@ -116,12 +143,13 @@
<LinearLayout <LinearLayout
android:id="@android:id/empty" android:id="@android:id/empty"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_marginTop="30dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:visibility="gone" android:visibility="gone"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
tools:visibility="visible"> tools:visibility="visible">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView

View file

@ -277,6 +277,7 @@
<string name="no_purchase_found">No purchase found.</string> <string name="no_purchase_found">No purchase found.</string>
<string name="no_results">No results</string> <string name="no_results">No results</string>
<string name="no_songs">You have no songs</string> <string name="no_songs">You have no songs</string>
<string name="no_search_results">No search results</string>
<string name="normal">Normal</string> <string name="normal">Normal</string>
<string name="normal_lyrics">Normal lyrics</string> <string name="normal_lyrics">Normal lyrics</string>
<string name="not_listed_in_media_store"><![CDATA[<b>%s</b> is not listed in the media store.]]></string> <string name="not_listed_in_media_store"><![CDATA[<b>%s</b> is not listed in the media store.]]></string>
@ -365,6 +366,7 @@
<string name="pref_summary_open_source_licences">License details for open source software</string> <string name="pref_summary_open_source_licences">License details for open source software</string>
<string name="pref_summary_pause_history">When enabled, newly played songs won\'t show in history</string> <string name="pref_summary_pause_history">When enabled, newly played songs won\'t show in history</string>
<string name="pref_summary_remember_tab">Navigate to the last used tab on start</string> <string name="pref_summary_remember_tab">Navigate to the last used tab on start</string>
<string name="pref_summary_enable_search_playlist">Show a search field in a playlist</string>
<string name="pref_summary_show_lyrics">Display synced lyrics over album cover</string> <string name="pref_summary_show_lyrics">Display synced lyrics over album cover</string>
<string name="pref_summary_suggestions">Show New Music Mix on homescreen</string> <string name="pref_summary_suggestions">Show New Music Mix on homescreen</string>
<string name="pref_summary_swipe_anywhere_now_playing">Enables changing song by swiping anywhere on the now playing screen</string> <string name="pref_summary_swipe_anywhere_now_playing">Enables changing song by swiping anywhere on the now playing screen</string>
@ -412,6 +414,7 @@
<string name="pref_title_open_source_licences">Open source licences</string> <string name="pref_title_open_source_licences">Open source licences</string>
<string name="pref_title_pause_history">Pause history</string> <string name="pref_title_pause_history">Pause history</string>
<string name="pref_title_remember_tab">Remember last tab</string> <string name="pref_title_remember_tab">Remember last tab</string>
<string name="pref_title_enable_search_playlist">Enable search in playlist</string>
<string name="pref_title_show_lyrics">Show lyrics</string> <string name="pref_title_show_lyrics">Show lyrics</string>
<string name="pref_title_suggestions">Show suggestions</string> <string name="pref_title_suggestions">Show suggestions</string>
<string name="pref_title_swipe_anywhere_now_playing">Swipe anywhere to change song</string> <string name="pref_title_swipe_anywhere_now_playing">Swipe anywhere to change song</string>

View file

@ -67,6 +67,13 @@
android:summary="@string/pref_summary_remember_tab" android:summary="@string/pref_summary_remember_tab"
android:title="@string/pref_title_remember_tab" /> android:title="@string/pref_title_remember_tab" />
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
android:defaultValue="true"
android:key="enable_search_playlist"
android:layout="@layout/list_item_view_switch"
android:summary="@string/pref_summary_enable_search_playlist"
android:title="@string/pref_title_enable_search_playlist" />
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference <code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference
android:defaultValue="0" android:defaultValue="0"
android:entries="@array/pref_tab_text_mode_titles" android:entries="@array/pref_tab_text_mode_titles"

View file

@ -18,8 +18,11 @@ android {
abortOnError false abortOnError false
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_17 sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_21
}
kotlinOptions {
jvmTarget = "21"
} }
} }

View file

@ -5,7 +5,7 @@ jetradarmobile_androidSnowfall_version = "1.2.1"
tankery_circularSeekBar_version = "1.4.2" tankery_circularSeekBar_version = "1.4.2"
retrofit_version = "2.9.0" retrofit_version = "2.9.0"
afollestad_dialog_version = "3.3.0" afollestad_dialog_version = "3.3.0"
coreSplashscreen = "1.0.0" coreSplashscreen = "1.0.1"
customactivityoncrash = "2.4.0" customactivityoncrash = "2.4.0"
fadingedgelayout = "1.0.0" fadingedgelayout = "1.0.0"
google_featureDelivery_version = "2.1.0" google_featureDelivery_version = "2.1.0"
@ -15,30 +15,30 @@ chrisbanes_insetter_version = "0.6.1"
jaudiotagger = "2.3.15" jaudiotagger = "2.3.15"
yslibrary_keyboardvisibilityevent_version = "3.0.0-RC3" yslibrary_keyboardvisibilityevent_version = "3.0.0-RC3"
koinAndroid = "3.4.0" koinAndroid = "3.4.0"
kotlinGradlePlugin = "1.8.10" kotlinGradlePlugin = "1.9.22"
kotlinxCoroutinesAndroid = "1.6.4" kotlinxCoroutinesAndroid = "1.8.1"
android_tab_library_version = "2.0.3" android_tab_library_version = "2.2.0"
fast_scroll_libraryVersion = "1.2.0" fast_scroll_libraryVersion = "1.2.0"
lifecycle_version = "2.6.1" lifecycle_version = "2.7.0"
okhttp3_loggingInterceptor_version = "5.0.0-alpha.9" okhttp3_loggingInterceptor_version = "5.0.0-alpha.9"
afollestad_materialCab_version = "2.0.1" afollestad_materialCab_version = "2.0.1"
materialIntro = "2.0.0" materialIntro = "2.0.0"
mediarouter = "1.3.1" mediarouter = "1.6.0"
nanohttpd = "2.3.1" nanohttpd = "2.3.1"
navigation_version = "2.5.3" navigation_version = "2.7.6"
mdc_version = "1.9.0-beta01" mdc_version = "1.9.0"
glide_version = "4.15.1" glide_version = "4.15.1"
orgEclipseEgitGithubCore = "2.1.5" orgEclipseEgitGithubCore = "2.1.5"
playServicesCastFramework = "21.3.0" playServicesCastFramework = "21.3.0"
preference_version = "1.2.0" preference_version = "1.2.1"
appcompat_version = "1.6.1" appcompat_version = "1.6.1"
google_play_review_version = "2.0.1" google_play_review_version = "2.0.1"
room_version = "2.5.1" room_version = "2.6.1"
core_version = "1.10.0-rc01" core_version = "1.12.0"
#plugins #plugins
devTools_ksp_version = "1.8.10-1.0.9" devTools_ksp_version = "1.9.22-1.0.17"
ben_manes_versoin = "0.46.0" ben_manes_versoin = "0.46.0"
slidableactivity = "2.1.0" slidableactivity = "2.1.0"
@ -111,9 +111,9 @@ nanohttpd = { group = "org.nanohttpd", name = "nanohttpd", version.ref = "nanoht
#androidx #androidx
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat_version" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat_version" }
androidx-annotation = { group = "androidx.annotation", name = "annotation", version = "1.6.0" } androidx-annotation = { group = "androidx.annotation", name = "annotation", version = "1.7.1" }
androidx-constraintLayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" } androidx-constraintLayout = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.4" }
androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version = "1.3.0" } androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version = "1.3.2" }
androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference_version" } androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference_version" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core_version" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core_version" }
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }