I am developing music app but when I run project only progressbar loading in my fragment not showing actual data I want to know exactly where I am making mistake
below my HomeFragment.kt
@AndroidEntryPoint
class HomeFragment : Fragment() {
lateinit var mainViewModel: MainViewModel
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
@Inject
lateinit var songAdapter: SongAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// inflate the layout and bind to the _binding
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mainViewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java]
setupRecyclerView()
subscribeToObservers()
songAdapter.setOnItemClickListener {
mainViewModel.playOrToggleSong(it)
}
}
private fun setupRecyclerView() = binding.rvAllSongs.apply {
adapter = songAdapter
layoutManager = LinearLayoutManager(requireContext())
}
private fun subscribeToObservers() {
mainViewModel.mediaItems.observe(viewLifecycleOwner) { result ->
when(result.status) {
Status.SUCCESS -> {
binding.allSongsProgressBar.isVisible = false
result.data?.let { songs ->
songAdapter.songs = songs
}
}
Status.ERROR -> Unit
Status.LOADING -> binding.allSongsProgressBar.isVisible = true
}
}
}
}
below my fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<ProgressBar
android:id="@ id/allSongsProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@ id/rvAllSongs"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
below list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@ id/ivItemImage"
android:layout_width="75dp"
android:layout_height="75dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@ id/tvPrimary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:gravity="center|left"
android:text="Playlist Title"
android:layout_marginTop="8dp"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@ id/ivItemImage"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@ id/tvSecondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="100 Songs"
android:textSize="14sp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@ id/ivItemImage"
app:layout_constraintTop_toBottomOf="@ id/tvPrimary" />
</androidx.constraintlayout.widget.ConstraintLayout>
below my Songadapter class
class SongAdapter @Inject constructor(
private val glide: RequestManager
) : RecyclerView.Adapter<SongAdapter.SongViewHolder>() {
//private lateinit var binding:ListItemBinding
inner class SongViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root)
private val diffCallback = object : DiffUtil.ItemCallback<Song>() {
override fun areItemsTheSame(oldItem: Song, newItem: Song): Boolean {
return oldItem.mediaId == newItem.mediaId
}
override fun areContentsTheSame(oldItem: Song, newItem: Song): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
}
private val differ = AsyncListDiffer(this, diffCallback)
var songs: List<Song>
get() = differ.currentList
set(value) = differ.submitList(value)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SongViewHolder(binding)
}
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
val song = songs[position]
holder.binding.apply {
tvPrimary.text = song.title
tvSecondary.text = song.subtitle
glide.load(song.imageUrl).into(ivItemImage)
holder.binding.root.setOnClickListener {
onItemClickListener?.let { click ->
click(song)
}
}
}
}
private var onItemClickListener: ((Song) -> Unit)? = null
fun setOnItemClickListener(listener: (Song) -> Unit) {
onItemClickListener = listener
}
override fun getItemCount(): Int {
return songs.size
}
}
below my MainViewModel.kt class
@HiltViewModel
class MainViewModel @Inject constructor(
private val musicServiceConnection: MusicServiceConnection
) : ViewModel() {
private val _mediaItems = MutableLiveData<Resource<List<Song>>>()
val mediaItems: LiveData<Resource<List<Song>>> = _mediaItems
val isConnected = musicServiceConnection.isConnected
val networkError = musicServiceConnection.networkError
val curPlayingSong = musicServiceConnection.curPlayingSong
val playbackState = musicServiceConnection.playbackState
init {
_mediaItems.postValue(Resource.loading(null))
musicServiceConnection.subscribe(
MEDIA_ROOT_ID,
object : MediaBrowserCompat.SubscriptionCallback() {
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowserCompat.MediaItem>
) {
super.onChildrenLoaded(parentId, children)
val items = children.map {
Song(
it.mediaId!!,
it.description.title.toString(),
it.description.subtitle.toString(),
it.description.mediaUri.toString(),
it.description.iconUri.toString()
)
}
_mediaItems.postValue(Resource.success(items))
}
})
}
fun skipToNextSong() {
musicServiceConnection.transportControls.skipToNext()
}
fun skipToPreviousSong() {
musicServiceConnection.transportControls.skipToPrevious()
}
fun seekTo(pos: Long) {
musicServiceConnection.transportControls.seekTo(pos)
}
fun playOrToggleSong(mediaItem: Song, toggle: Boolean = false) {
val isPrepared = playbackState.value?.isPrepared ?: false
if (isPrepared && mediaItem.mediaId == curPlayingSong.value?.getString(METADATA_KEY_MEDIA_ID)) {
playbackState.value?.let { playbackState ->
when {
playbackState.isPlaying -> if (toggle) musicServiceConnection.transportControls.pause()
playbackState.isPlayEnabled -> musicServiceConnection.transportControls.play()
else -> Unit
}
}
} else {
musicServiceConnection.transportControls.playFromMediaId(mediaItem.mediaId, null)
}
}
override fun onCleared() {
super.onCleared()
musicServiceConnection.unsubscribe(
MEDIA_ROOT_ID,
object : MediaBrowserCompat.SubscriptionCallback() {})
}
}
Below MusicServiceConnection.kt
class MusicServiceConnection(
context: Context
) {
private val _isConnected = MutableLiveData<Event<Resource<Boolean>>>()
val isConnected: LiveData<Event<Resource<Boolean>>> = _isConnected
private val _networkError = MutableLiveData<Event<Resource<Boolean>>>()
val networkError: LiveData<Event<Resource<Boolean>>> = _networkError
private val _playbackState = MutableLiveData<PlaybackStateCompat?>()
val playbackState: LiveData<PlaybackStateCompat?> = _playbackState
private val _curPlayingSong = MutableLiveData<MediaMetadataCompat?>()
val curPlayingSong: LiveData<MediaMetadataCompat?> = _curPlayingSong
lateinit var mediaController: MediaControllerCompat
private val mediaBrowserConnectionCallback = MediaBrowserConnectionCallback(context)
private val mediaBrowser = MediaBrowserCompat(
context,
ComponentName(
context,
MusicService::class.java
),
mediaBrowserConnectionCallback,
null
).apply {
connect()
}
val transportControls: MediaControllerCompat.TransportControls
get() = mediaController.transportControls
fun subscribe(parentId:String, callback: MediaBrowserCompat.SubscriptionCallback){
mediaBrowser.subscribe(parentId, callback)
}
fun unsubscribe(parentId:String, callback: MediaBrowserCompat.SubscriptionCallback){
mediaBrowser.unsubscribe(parentId, callback)
}
private inner class MediaBrowserConnectionCallback(
private val context: Context
) : MediaBrowserCompat.ConnectionCallback() {
override fun onConnected() {
mediaController = MediaControllerCompat(context, mediaBrowser.sessionToken).apply {
registerCallback(MediaControllerCallback())
}
_isConnected.postValue(Event(Resource.success(true)))
}
override fun onConnectionSuspended() {
_isConnected.postValue(
Event(
Resource.error(
"The connection was suspended",
false
)
)
)
}
override fun onConnectionFailed() {
_isConnected.postValue(
Event(
Resource.error(
"Could connect media browser",
false
)
)
)
}
}
private inner class MediaControllerCallback : MediaControllerCompat.Callback() {
override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {
_playbackState.postValue(state)
}
override fun onMetadataChanged(metadata: MediaMetadataCompat?) {
_curPlayingSong.postValue(metadata)
}
override fun onSessionEvent(event: String?, extras: Bundle?) {
super.onSessionEvent(event, extras)
when (event) {
NETWORK_ERROR -> _networkError.postValue(
Event(
Resource.error(
"Could not connect to the server. Please check your internet connection",
null
)
)
)
}
}
override fun onSessionDestroyed() {
mediaBrowserConnectionCallback.onConnectionSuspended()
}
}
}
what I have tried I have done debugging it is not coming till on status== success and one rror it skipping success and one rror in debug mode it is only showing what is happening in onl oading
I want to know where exactly I am making mistake and why data not showing
CodePudding user response:
problem was in my musicservice in onl oadchildren previously it was written like this
override fun onl oadChildren(
parentId: String,
result: Result<MutableList<MediaBrowserCompat.MediaItem>>
) {
when (parentId) {
MEDIA_ROOT_ID -> {
val resultsSent = firebaseMusicSource.whenReady { isInitialized ->
if (isPlayerInitialized) {
result.sendResult(firebaseMusicSource.asMediaItems())
if (!isInitialized && firebaseMusicSource.songs.isNotEmpty()) {
preparePlayer(
firebaseMusicSource.songs,
firebaseMusicSource.songs[0],
false
)
isPlayerInitialized = true
}
} else {
mediaSession.sendSessionEvent(NETWORK_ERROR, null)
result.sendResult(null)
}
}
if (!resultsSent) {
result.detach()
}
}
}
}
}
bt I changed inside if block like this if (isInitialized) then it solved my problem