Merge branch 'room-playlist' of https://github.com/h4h13/RetroMusicPlayer into room-playlist

main
Hemanth S 2020-08-31 18:04:18 +05:30
commit 6f07548023
21 changed files with 179 additions and 243 deletions

View File

@ -10,21 +10,15 @@ import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.extensions.findNavController import code.name.monkey.retromusic.extensions.findNavController
import code.name.monkey.retromusic.helper.MusicPlayerRemote.openAndShuffleQueue import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicPlayerRemote.openQueue
import code.name.monkey.retromusic.helper.MusicPlayerRemote.playFromUri
import code.name.monkey.retromusic.helper.MusicPlayerRemote.shuffleMode
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.PlaylistSongsLoader.getPlaylistSongList import code.name.monkey.retromusic.repository.PlaylistSongsLoader
import code.name.monkey.retromusic.repository.Repository
import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.AppRater.appLaunched import code.name.monkey.retromusic.util.AppRater.appLaunched
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import java.util.*
class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeListener { class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeListener {
companion object { companion object {
@ -33,7 +27,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
const val APP_UPDATE_REQUEST_CODE = 9002 const val APP_UPDATE_REQUEST_CODE = 9002
} }
private val repository by inject<Repository>()
private var blockRequestPermissions = false private var blockRequestPermissions = false
override fun createContentView(): View { override fun createContentView(): View {
@ -94,61 +87,68 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
override fun onServiceConnected() { override fun onServiceConnected() {
super.onServiceConnected() super.onServiceConnected()
handlePlaybackIntent(intent)
}
private fun handlePlaybackIntent(intent: Intent?) {
if (intent == null) { if (intent == null) {
return return
} }
val uri = intent.data handlePlaybackIntent(intent)
val mimeType = intent.type }
var handled = false
if (intent.action != null && (intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH) private fun handlePlaybackIntent(intent: Intent) {
) { lifecycleScope.launch(IO) {
val songs: List<Song> = val uri = intent.data
getSongs(intent.extras!!) val mimeType = intent.type
if (shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) { var handled = false
openAndShuffleQueue(songs, true) if (intent.action != null &&
} else { intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
openQueue(songs, 0, true) ) {
} val songs: List<Song> = getSongs(intent.extras!!)
handled = true if (MusicPlayerRemote.shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) {
} MusicPlayerRemote.openAndShuffleQueue(songs, true)
if (uri != null && uri.toString().isNotEmpty()) { } else {
playFromUri(uri) MusicPlayerRemote.openQueue(songs, 0, true)
handled = true }
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt()
if (id >= 0) {
val position = intent.getIntExtra("position", 0)
val songs: List<Song> =
ArrayList(getPlaylistSongList(this, id))
openQueue(songs, position, true)
handled = true handled = true
} }
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) { if (uri != null && uri.toString().isNotEmpty()) {
val id = parseIdFromIntent(intent, "albumId", "album").toInt() MusicPlayerRemote.playFromUri(uri)
if (id >= 0) { handled = true
lifecycleScope.launch(Dispatchers.Main) { } else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt()
if (id >= 0) {
val position = intent.getIntExtra("position", 0) val position = intent.getIntExtra("position", 0)
openQueue(repository.albumByIdAsync(id).songs!!, position, true) val songs: List<Song> =
PlaylistSongsLoader.getPlaylistSongList(this@MainActivity, id)
MusicPlayerRemote.openQueue(songs, position, true)
handled = true
}
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "albumId", "album").toInt()
if (id >= 0) {
val position = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue(
libraryViewModel.albumById(id).songs!!,
position,
true
)
handled = true
}
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "artistId", "artist").toInt()
if (id >= 0) {
val position = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue(
libraryViewModel.artistById(id).songs,
position,
true
)
handled = true handled = true
} }
} }
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { if (handled) {
val id = parseIdFromIntent(intent, "artistId", "artist").toInt() setIntent(Intent())
if (id >= 0) {
lifecycleScope.launch {
val position = intent.getIntExtra("position", 0)
openQueue(repository.artistById(id).songs, position, true)
handled = true
}
} }
} }
if (handled) {
setIntent(Intent())
}
} }
private fun parseIdFromIntent( private fun parseIdFromIntent(

View File

@ -33,7 +33,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
} }
private val libraryViewModel by viewModel<LibraryViewModel>() protected val libraryViewModel by viewModel<LibraryViewModel>()
private lateinit var behavior: RetroBottomSheetBehavior<FrameLayout> private lateinit var behavior: RetroBottomSheetBehavior<FrameLayout>
private var miniPlayerFragment: MiniPlayerFragment? = null private var miniPlayerFragment: MiniPlayerFragment? = null
private var cps: NowPlayingScreen? = null private var cps: NowPlayingScreen? = null
@ -78,6 +78,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
libraryViewModel.paletteColorLiveData.observe(this, Observer { libraryViewModel.paletteColorLiveData.observe(this, Observer {
this.paletteColor = it this.paletteColor = it
miniPlayerFragment?.updateProgressBar(it)
onPaletteColorChanged() onPaletteColorChanged()
}) })
} }

View File

@ -5,7 +5,7 @@ import androidx.room.RoomDatabase
@Database( @Database(
entities = [PlaylistEntity::class, SongEntity::class, HistoryEntity::class, PlayCountEntity::class, BlackListStoreEntity::class], entities = [PlaylistEntity::class, SongEntity::class, HistoryEntity::class, PlayCountEntity::class, BlackListStoreEntity::class],
version = 19, version = 20,
exportSchema = false exportSchema = false
) )
abstract class RetroDatabase : RoomDatabase() { abstract class RetroDatabase : RoomDatabase() {

View File

@ -15,15 +15,12 @@ import code.name.monkey.retromusic.extensions.materialDialog
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType.Playlists import code.name.monkey.retromusic.fragments.ReloadType.Playlists
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.RealRepository
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class AddToRetroPlaylist : BottomSheetDialogFragment() { class AddToRetroPlaylist : BottomSheetDialogFragment() {
private val repository by inject<RealRepository>()
private val libraryViewModel by sharedViewModel<LibraryViewModel>() private val libraryViewModel by sharedViewModel<LibraryViewModel>()
companion object { companion object {
@ -58,7 +55,7 @@ class AddToRetroPlaylist : BottomSheetDialogFragment() {
} else { } else {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val songEntities = songs.toSongEntity(playlistEntities[which - 1]) val songEntities = songs.toSongEntity(playlistEntities[which - 1])
repository.insertSongs(songEntities) libraryViewModel.insertSongs(songEntities)
libraryViewModel.forceReload(Playlists) libraryViewModel.forceReload(Playlists)
} }
} }

View File

@ -5,7 +5,6 @@ import android.os.Bundle
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.EXTRA_PLAYLIST import code.name.monkey.retromusic.EXTRA_PLAYLIST
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
@ -14,14 +13,10 @@ import code.name.monkey.retromusic.extensions.extraNotNull
import code.name.monkey.retromusic.extensions.materialDialog import code.name.monkey.retromusic.extensions.materialDialog
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.repository.Repository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class DeleteRetroPlaylist : DialogFragment() { class DeleteRetroPlaylist : DialogFragment() {
private val repository by inject<Repository>()
private val libraryViewModel by sharedViewModel<LibraryViewModel>() private val libraryViewModel by sharedViewModel<LibraryViewModel>()
companion object { companion object {
@ -63,11 +58,9 @@ class DeleteRetroPlaylist : DialogFragment() {
.setMessage(message) .setMessage(message)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.action_delete) { _, _ -> .setPositiveButton(R.string.action_delete) { _, _ ->
lifecycleScope.launch(Dispatchers.IO) { libraryViewModel.deleteSongsFromPlaylist(playlists)
repository.deleteSongsFromPlaylist(playlists) libraryViewModel.deleteRoomPlaylist(playlists)
repository.deleteRoomPlaylist(playlists) libraryViewModel.forceReload(ReloadType.Playlists)
libraryViewModel.forceReload(ReloadType.Playlists)
}
} }
.create() .create()
.colorButtons() .colorButtons()

View File

@ -5,7 +5,6 @@ import android.os.Bundle
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.EXTRA_SONG import code.name.monkey.retromusic.EXTRA_SONG
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
@ -14,14 +13,9 @@ import code.name.monkey.retromusic.extensions.extraNotNull
import code.name.monkey.retromusic.extensions.materialDialog import code.name.monkey.retromusic.extensions.materialDialog
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType.Playlists import code.name.monkey.retromusic.fragments.ReloadType.Playlists
import code.name.monkey.retromusic.repository.Repository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class RemoveSongFromPlaylistDialog : DialogFragment() { class RemoveSongFromPlaylistDialog : DialogFragment() {
private val repository by inject<Repository>()
private val libraryViewModel by sharedViewModel<LibraryViewModel>() private val libraryViewModel by sharedViewModel<LibraryViewModel>()
companion object { companion object {
@ -65,11 +59,8 @@ class RemoveSongFromPlaylistDialog : DialogFragment() {
return materialDialog(pair.first) return materialDialog(pair.first)
.setMessage(pair.second) .setMessage(pair.second)
.setPositiveButton(R.string.remove_action) { _, _ -> .setPositiveButton(R.string.remove_action) { _, _ ->
lifecycleScope.launch(Dispatchers.IO) { libraryViewModel.deleteSongsInPlaylist(songs)
//repository.removeSongFromPlaylist(songs) libraryViewModel.forceReload(Playlists)
repository.deleteSongsInPlaylist(songs)
libraryViewModel.forceReload(Playlists)
}
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.create() .create()

View File

@ -5,7 +5,6 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
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.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
@ -15,16 +14,12 @@ import code.name.monkey.retromusic.extensions.extraNotNull
import code.name.monkey.retromusic.extensions.materialDialog import code.name.monkey.retromusic.extensions.materialDialog
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.repository.Repository
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class RenameRetroPlaylistDialog : DialogFragment() { class RenameRetroPlaylistDialog : DialogFragment() {
private val repository by inject<Repository>()
private val libraryViewModel by sharedViewModel<LibraryViewModel>() private val libraryViewModel by sharedViewModel<LibraryViewModel>()
companion object { companion object {
@ -50,10 +45,8 @@ class RenameRetroPlaylistDialog : DialogFragment() {
.setPositiveButton(R.string.action_rename) { _, _ -> .setPositiveButton(R.string.action_rename) { _, _ ->
val name = inputEditText.text.toString() val name = inputEditText.text.toString()
if (name.isNotEmpty()) { if (name.isNotEmpty()) {
lifecycleScope.launch(Dispatchers.IO) { libraryViewModel.renameRoomPlaylist(playlistEntity.playListId, name)
repository.renameRoomPlaylist(playlistEntity.playListId, name) libraryViewModel.forceReload(ReloadType.Playlists)
libraryViewModel.forceReload(ReloadType.Playlists)
}
} else { } else {
nameContainer.error = "Playlist name should'nt be empty" nameContainer.error = "Playlist name should'nt be empty"
} }

View File

@ -35,6 +35,8 @@ import code.name.monkey.retromusic.R
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.ProgressIndicator
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
fun Int.ripAlpha(): Int { fun Int.ripAlpha(): Int {
@ -144,6 +146,21 @@ fun TextInputLayout.accentColor() {
isHintAnimationEnabled = true isHintAnimationEnabled = true
} }
fun ProgressIndicator.accentColor() {
val accentColor = ThemeStore.accentColor(context)
indicatorColors = intArrayOf(accentColor)
trackColor = ColorUtil.withAlpha(accentColor, 0.2f)
}
fun ProgressIndicator.applyColor(color: Int) {
indicatorColors = intArrayOf(color)
trackColor = ColorUtil.withAlpha(color, 0.2f)
}
fun TextInputEditText.accentColor() {
}
fun AppCompatImageView.accentColor(): Int { fun AppCompatImageView.accentColor(): Int {
return ThemeStore.accentColor(context) return ThemeStore.accentColor(context)
} }

View File

@ -4,8 +4,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.db.PlaylistEntity
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.fragments.ReloadType.* import code.name.monkey.retromusic.fragments.ReloadType.*
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
@ -133,6 +136,38 @@ class LibraryViewModel(
println("onShuffleModeChanged") println("onShuffleModeChanged")
} }
fun shuffleSongs() = viewModelScope.launch(IO) {
val songs = repository.allSongs()
MusicPlayerRemote.openAndShuffleQueue(
songs,
true
)
}
fun renameRoomPlaylist(playListId: Int, name: String) = viewModelScope.launch(IO) {
repository.renameRoomPlaylist(playListId, name)
}
fun deleteSongsInPlaylist(songs: List<SongEntity>) = viewModelScope.launch(IO) {
repository.deleteSongsInPlaylist(songs)
}
fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) = viewModelScope.launch(IO) {
repository.deleteSongsFromPlaylist(playlists)
}
fun deleteRoomPlaylist(playlists: List<PlaylistEntity>) = viewModelScope.launch(IO) {
repository.deleteRoomPlaylist(playlists)
}
suspend fun albumById(id: Int) = repository.albumById(id)
suspend fun artistById(id: Int) = repository.artistById(id)
suspend fun favoritePlaylist() = repository.favoritePlaylist()
suspend fun isFavoriteSong(song: SongEntity) = repository.isFavoriteSong(song)
suspend fun insertSongs(songs: List<SongEntity>) = repository.insertSongs(songs)
suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
repository.removeSongFromPlaylist(songEntity)
} }
enum class ReloadType { enum class ReloadType {

View File

@ -11,18 +11,14 @@ import android.view.GestureDetector
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.extensions.textColorPrimary
import code.name.monkey.retromusic.extensions.textColorSecondary
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.ViewUtil
import kotlinx.android.synthetic.main.fragment_mini_player.* import kotlinx.android.synthetic.main.fragment_mini_player.*
import kotlin.math.abs import kotlin.math.abs
@ -67,7 +63,7 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
private fun setUpMiniPlayer() { private fun setUpMiniPlayer() {
setUpPlayPauseButton() setUpPlayPauseButton()
ViewUtil.setProgressDrawable(progressBar, ThemeStore.accentColor(requireContext())) progressBar.accentColor()
} }
private fun setUpPlayPauseButton() { private fun setUpPlayPauseButton() {
@ -129,6 +125,10 @@ open class MiniPlayerFragment : AbsMusicServiceFragment(R.layout.fragment_mini_p
} }
} }
fun updateProgressBar(paletteColor: Int) {
progressBar.applyColor(paletteColor)
}
class FlingPlayBackController(context: Context) : View.OnTouchListener { class FlingPlayBackController(context: Context) : View.OnTouchListener {
private var flingPlayBackController: GestureDetector private var flingPlayBackController: GestureDetector

View File

@ -14,8 +14,7 @@ import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
class AlbumsFragment : class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
AlbumClickListener { AlbumClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@ -12,17 +12,10 @@ import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
import code.name.monkey.retromusic.extensions.findActivityNavController import code.name.monkey.retromusic.extensions.findActivityNavController
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
class ArtistsFragment : class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(), ArtistClickListener {
MainActivityFragmentCallbacks, ArtistClickListener {
override fun handleBackPress(): Boolean {
return false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -25,7 +25,7 @@ import code.name.monkey.retromusic.dialogs.*
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.whichFragment import code.name.monkey.retromusic.extensions.whichFragment
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType.Playlists import code.name.monkey.retromusic.fragments.ReloadType
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
import code.name.monkey.retromusic.interfaces.PaletteColorHolder import code.name.monkey.retromusic.interfaces.PaletteColorHolder
@ -40,7 +40,6 @@ import kotlinx.coroutines.Dispatchers.Main
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 org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.io.FileNotFoundException import java.io.FileNotFoundException
@ -160,22 +159,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return false return false
} }
protected open fun toggleFavorite(song: Song) {
lifecycleScope.launch(IO) {
val playlist: PlaylistEntity? = repository.favoritePlaylist()
if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
if (isFavorite) {
repository.removeSongFromPlaylist(songEntity)
} else {
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
}
}
libraryViewModel.forceReload(Playlists)
requireContext().sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
}
}
abstract fun playerToolbar(): Toolbar? abstract fun playerToolbar(): Toolbar?
@ -197,25 +180,44 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
updateLyrics() updateLyrics()
} }
protected open fun toggleFavorite(song: Song) {
lifecycleScope.launch(IO) {
val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty()
if (isFavorite) {
libraryViewModel.removeSongFromPlaylist(songEntity)
} else {
libraryViewModel.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
}
}
libraryViewModel.forceReload(ReloadType.Playlists)
requireContext().sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
}
}
fun updateIsFavorite() { fun updateIsFavorite() {
lifecycleScope.launch(IO) { lifecycleScope.launch(IO) {
val playlist: PlaylistEntity = repository.favoritePlaylist() val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist()
val song = MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId) if (playlist != null) {
val isFavorite = repository.isFavoriteSong(song).isNotEmpty() val song = MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
withContext(Main) { val isFavorite = libraryViewModel.isFavoriteSong(song).isNotEmpty()
val icon = if (isFavorite) R.drawable.ic_favorite withContext(Main) {
else R.drawable.ic_favorite_border val icon =
val drawable = if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
RetroUtil.getTintedVectorDrawable( val drawable =
requireContext(), RetroUtil.getTintedVectorDrawable(
icon, requireContext(),
toolbarIconColor() icon,
) toolbarIconColor()
if (playerToolbar() != null) { )
playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite) if (playerToolbar() != null) {
?.setIcon(drawable)?.title = playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite)
if (isFavorite) getString(R.string.action_remove_from_favorites) ?.setIcon(drawable)?.title =
else getString(R.string.action_add_to_favorites) if (isFavorite) getString(R.string.action_remove_from_favorites)
else getString(R.string.action_add_to_favorites)
}
} }
} }
} }
@ -247,17 +249,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
open fun setLyrics(l: Lyrics?) { open fun setLyrics(l: Lyrics?) {
} }
private val repository by inject<RealRepository>()
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
lifecycleScope.launch(IO) {
if (repository.checkPlaylistExists(getString(R.string.favorites)).isEmpty()) {
repository.createPlaylist(PlaylistEntity(getString(R.string.favorites)))
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (PreferenceUtil.isFullScreenMode && if (PreferenceUtil.isFullScreenMode &&

View File

@ -21,14 +21,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.GenreAdapter import code.name.monkey.retromusic.adapter.GenreAdapter
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(), class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>() {
MainActivityFragmentCallbacks {
override fun handleBackPress(): Boolean {
return false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -19,7 +19,6 @@ import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.retromusic.HISTORY_PLAYLIST import code.name.monkey.retromusic.HISTORY_PLAYLIST
import code.name.monkey.retromusic.LAST_ADDED_PLAYLIST import code.name.monkey.retromusic.LAST_ADDED_PLAYLIST
@ -31,25 +30,19 @@ import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest
import code.name.monkey.retromusic.glide.UserProfileGlideRequest import code.name.monkey.retromusic.glide.UserProfileGlideRequest
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.repository.Repository
import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.abs_playlists.* import kotlinx.android.synthetic.main.abs_playlists.*
import kotlinx.android.synthetic.main.fragment_banner_home.* import kotlinx.android.synthetic.main.fragment_banner_home.*
import kotlinx.android.synthetic.main.home_content.* import kotlinx.android.synthetic.main.home_content.*
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class HomeFragment : class HomeFragment :
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) { AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) {
private val repository by inject<Repository>()
private val libraryViewModel: LibraryViewModel by sharedViewModel() private val libraryViewModel: LibraryViewModel by sharedViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setStatusBarColorAuto(view) setStatusBarColorAuto(view)
@ -77,12 +70,7 @@ class HomeFragment :
} }
actionShuffle.setOnClickListener { actionShuffle.setOnClickListener {
lifecycleScope.launch { libraryViewModel.shuffleSongs()
MusicPlayerRemote.openAndShuffleQueue(
repository.allSongs(),
true
)
}
} }
history.setOnClickListener { history.setOnClickListener {

View File

@ -5,7 +5,6 @@ import android.annotation.SuppressLint
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
@ -18,7 +17,6 @@ import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
@ -61,7 +59,6 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
private var playingQueueAdapter: PlayingQueueAdapter? = null private var playingQueueAdapter: PlayingQueueAdapter? = null
private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null
private lateinit var linearLayoutManager: LinearLayoutManager private lateinit var linearLayoutManager: LinearLayoutManager
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() { private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() {
@ -218,9 +215,8 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun toggleFavorite(song: Song) { override fun toggleFavorite(song: Song) {
super.toggleFavorite(song) super.toggleFavorite(song)
MusicUtil.toggleFavorite(requireContext(), song)
if (song.id == MusicPlayerRemote.currentSong.id) { if (song.id == MusicPlayerRemote.currentSong.id) {
updateFavorite() updateIsFavorite()
} }
} }
@ -268,6 +264,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
override fun onQueueChanged() { override fun onQueueChanged() {
super.onQueueChanged() super.onQueueChanged()
updateLabel() updateLabel()
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue)
} }
private fun updateSong() { private fun updateSong() {
@ -366,7 +363,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
} else { } else {
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
nextSong.apply { nextSong.apply {
text = "Next: $title" text = title
show() show()
} }
} }
@ -472,36 +469,4 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
} }
@SuppressLint("StaticFieldLeak")
private fun updateFavorite() {
if (updateIsFavoriteTask != null) {
updateIsFavoriteTask?.cancel(false)
}
updateIsFavoriteTask =
object : AsyncTask<Song, Void, Boolean>() {
override fun doInBackground(vararg params: Song): Boolean? {
val activity = activity
return if (activity != null) {
MusicUtil.isFavorite(requireActivity(), params[0])
} else {
cancel(false)
null
}
}
override fun onPostExecute(isFavorite: Boolean?) {
val activity = activity
if (activity != null) {
val res = if (isFavorite!!)
R.drawable.ic_favorite
else
R.drawable.ic_favorite_border
val drawable = TintHelper.createTintedDrawable(activity, res, Color.WHITE)
songFavourite?.setImageDrawable(drawable)
}
}
}.execute(MusicPlayerRemote.currentSong)
}
} }

View File

@ -7,15 +7,8 @@ import androidx.recyclerview.widget.GridLayoutManager
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
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
class PlaylistsFragment : class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>() {
AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>(),
MainActivityFragmentCallbacks {
override fun handleBackPress(): Boolean {
return false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -22,7 +22,6 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
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 com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
@ -33,13 +32,7 @@ import kotlinx.android.synthetic.main.activity_playing_queue.*
/** /**
* Created by hemanths on 2019-12-08. * Created by hemanths on 2019-12-08.
*/ */
class PlayingQueueFragment : class PlayingQueueFragment : AbsRecyclerViewFragment<PlayingQueueAdapter, LinearLayoutManager>() {
AbsRecyclerViewFragment<PlayingQueueAdapter, LinearLayoutManager>(),
MainActivityFragmentCallbacks {
override fun handleBackPress(): Boolean {
return false
}
private lateinit var wrappedAdapter: RecyclerView.Adapter<*> private lateinit var wrappedAdapter: RecyclerView.Adapter<*>
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null

View File

@ -9,15 +9,9 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
class SongsFragment : class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>() {
AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>(),
MainActivityFragmentCallbacks {
override fun handleBackPress(): Boolean {
return false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -23,7 +23,6 @@ import code.name.monkey.retromusic.model.AbsCustomPlaylist
import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.PlaylistSong import code.name.monkey.retromusic.model.PlaylistSong
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import java.util.*
/** /**
* Created by hemanths on 16/08/17. * Created by hemanths on 16/08/17.
@ -43,8 +42,8 @@ object PlaylistSongsLoader {
} }
@JvmStatic @JvmStatic
fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList<Song> { fun getPlaylistSongList(context: Context, playlistId: Int): List<Song> {
val songs = arrayListOf<Song>() val songs = mutableListOf<Song>()
val cursor = val cursor =
makePlaylistSongCursor( makePlaylistSongCursor(
context, context,

View File

@ -12,7 +12,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="48dp"> android:paddingBottom="48dp">
<fragment <fragment
android:id="@+id/playerAlbumCoverFragment" android:id="@+id/playerAlbumCoverFragment"
android:name="code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment" android:name="code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment"
android:layout_width="0dp" android:layout_width="0dp"
@ -74,7 +74,8 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="spread"> app:layout_constraintWidth_default="spread"
tools:background="@color/md_red_500">
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/queueIcon" android:id="@+id/queueIcon"
@ -100,8 +101,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_default="wrap" app:layout_constraintHeight_default="wrap"
app:layout_constraintStart_toEndOf="@id/queueIcon" app:layout_constraintStart_toEndOf="@id/queueIcon"
app:layout_constraintTop_toTopOf="@id/queueIcon" app:layout_constraintTop_toTopOf="@id/queueIcon" />
tools:text="@tools:sample/lorem/random" />
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/repeatButton" android:id="@+id/repeatButton"