Fix memory leak with PlaybackViewModel
Fix a memory leak involving a stray context that PlaybackViewModel would store.
This commit is contained in:
parent
85475b5c61
commit
3251b84e23
13 changed files with 29 additions and 57 deletions
|
@ -23,9 +23,10 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service android:name=".playback.PlaybackService"
|
<service
|
||||||
|
android:name=".playback.PlaybackService"
|
||||||
android:icon="@drawable/ic_launcher_foreground"
|
android:icon="@drawable/ic_launcher_foreground"
|
||||||
android:description="@string/description_service_playback"
|
android:description="@string/label_service_playback"
|
||||||
android:stopWithTask="false" />
|
android:stopWithTask="false" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
|
@ -1,5 +1,6 @@
|
||||||
package org.oxycblt.auxio
|
package org.oxycblt.auxio
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -15,6 +16,7 @@ import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
import org.oxycblt.auxio.library.LibraryFragment
|
import org.oxycblt.auxio.library.LibraryFragment
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
|
import org.oxycblt.auxio.playback.PlaybackService
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.songs.SongsFragment
|
import org.oxycblt.auxio.songs.SongsFragment
|
||||||
import org.oxycblt.auxio.theme.accent
|
import org.oxycblt.auxio.theme.accent
|
||||||
|
@ -23,9 +25,7 @@ import org.oxycblt.auxio.theme.getTransparentAccent
|
||||||
import org.oxycblt.auxio.theme.toColor
|
import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
class MainFragment : Fragment() {
|
class MainFragment : Fragment() {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
private val shownFragments = listOf(0, 1)
|
private val shownFragments = listOf(0, 1)
|
||||||
private val tabIcons = listOf(
|
private val tabIcons = listOf(
|
||||||
|
@ -105,6 +105,11 @@ class MainFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the playback service.
|
||||||
|
Intent(requireContext(), PlaybackService::class.java).also {
|
||||||
|
requireContext().startService(it)
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment Created.")
|
Log.d(this::class.simpleName, "Fragment Created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -22,9 +22,7 @@ class AlbumDetailFragment : Fragment() {
|
||||||
|
|
||||||
private val args: AlbumDetailFragmentArgs by navArgs()
|
private val args: AlbumDetailFragmentArgs by navArgs()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
|
|
@ -20,9 +20,7 @@ import org.oxycblt.auxio.theme.disable
|
||||||
class ArtistDetailFragment : Fragment() {
|
class ArtistDetailFragment : Fragment() {
|
||||||
private val args: ArtistDetailFragmentArgs by navArgs()
|
private val args: ArtistDetailFragmentArgs by navArgs()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
|
|
@ -21,9 +21,7 @@ class GenreDetailFragment : Fragment() {
|
||||||
|
|
||||||
private val args: GenreDetailFragmentArgs by navArgs()
|
private val args: GenreDetailFragmentArgs by navArgs()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
|
|
@ -35,9 +35,7 @@ import org.oxycblt.auxio.theme.resolveAttr
|
||||||
class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
|
|
||||||
private val libraryModel: LibraryViewModel by activityViewModels()
|
private val libraryModel: LibraryViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
|
|
@ -16,9 +16,7 @@ import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
|
|
||||||
class CompactPlaybackFragment : Fragment() {
|
class CompactPlaybackFragment : Fragment() {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
|
|
@ -22,9 +22,7 @@ import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
// TODO: Add a swipe-to-next-track function using a ViewPager
|
// TODO: Add a swipe-to-next-track function using a ViewPager
|
||||||
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireActivity().application)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement nav to artists/albums
|
// TODO: Implement nav to artists/albums
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
|
|
@ -139,12 +139,13 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotification(): Notification {
|
private fun createNotification(): Notification {
|
||||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val notificationManager =
|
||||||
|
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val channel = NotificationChannel(
|
val channel = NotificationChannel(
|
||||||
CHANNEL_ID,
|
CHANNEL_ID,
|
||||||
getString(R.string.description_playback),
|
getString(R.string.label_notif_playback),
|
||||||
NotificationManager.IMPORTANCE_DEFAULT
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
)
|
)
|
||||||
notificationManager.createNotificationChannel(channel)
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
@ -156,7 +157,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateCallback {
|
||||||
)
|
)
|
||||||
.setSmallIcon(R.drawable.ic_song)
|
.setSmallIcon(R.drawable.ic_song)
|
||||||
.setContentTitle(getString(R.string.app_name))
|
.setContentTitle(getString(R.string.app_name))
|
||||||
.setContentText(getString(R.string.description_playback))
|
.setContentText(getString(R.string.label_is_playing))
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
.setChannelId(CHANNEL_ID)
|
.setChannelId(CHANNEL_ID)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
package org.oxycblt.auxio.playback
|
package org.oxycblt.auxio.playback
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
|
@ -21,7 +18,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
// TODO: Implement Looping Modes
|
// TODO: Implement Looping Modes
|
||||||
// TODO: Implement User Queue
|
// TODO: Implement User Queue
|
||||||
// TODO: Implement Persistence through Bundles/Databases/Idk
|
// TODO: Implement Persistence through Bundles/Databases/Idk
|
||||||
class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackStateCallback {
|
class PlaybackViewModel() : ViewModel(), PlaybackStateCallback {
|
||||||
// Playback
|
// Playback
|
||||||
private val mSong = MutableLiveData<Song>()
|
private val mSong = MutableLiveData<Song>()
|
||||||
val song: LiveData<Song> get() = mSong
|
val song: LiveData<Song> get() = mSong
|
||||||
|
@ -66,13 +63,6 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta
|
||||||
private val playbackManager = PlaybackStateManager.getInstance()
|
private val playbackManager = PlaybackStateManager.getInstance()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Start the service from the ViewModel.
|
|
||||||
// Yes, I know ViewModels aren't supposed to deal with this stuff but for some
|
|
||||||
// reason it only works here.
|
|
||||||
Intent(context, PlaybackService::class.java).also {
|
|
||||||
context.startService(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
playbackManager.addCallback(this)
|
playbackManager.addCallback(this)
|
||||||
|
|
||||||
// If the PlaybackViewModel was cleared [signified by the PlaybackStateManager having a
|
// If the PlaybackViewModel was cleared [signified by the PlaybackStateManager having a
|
||||||
|
@ -233,6 +223,8 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restorePlaybackState() {
|
private fun restorePlaybackState() {
|
||||||
|
Log.d(this::class.simpleName, "Attempting to restore playback state.")
|
||||||
|
|
||||||
mSong.value = playbackManager.song
|
mSong.value = playbackManager.song
|
||||||
mPosition.value = playbackManager.position
|
mPosition.value = playbackManager.position
|
||||||
mQueue.value = playbackManager.queue
|
mQueue.value = playbackManager.queue
|
||||||
|
@ -240,15 +232,4 @@ class PlaybackViewModel(private val context: Context) : ViewModel(), PlaybackSta
|
||||||
mIsPlaying.value = playbackManager.isPlaying
|
mIsPlaying.value = playbackManager.isPlaying
|
||||||
mIsShuffling.value = playbackManager.isShuffling
|
mIsShuffling.value = playbackManager.isShuffling
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(private val context: Context) : ViewModelProvider.Factory {
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
|
||||||
if (modelClass.isAssignableFrom(PlaybackViewModel::class.java)) {
|
|
||||||
return PlaybackViewModel(context) as T
|
|
||||||
}
|
|
||||||
|
|
||||||
throw IllegalArgumentException("Unknown ViewModel class.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,7 @@ import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.toColor
|
import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
class QueueFragment : BottomSheetDialogFragment() {
|
class QueueFragment : BottomSheetDialogFragment() {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireActivity().application)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTheme(): Int = R.style.Theme_BottomSheetFix
|
override fun getTheme(): Int = R.style.Theme_BottomSheetFix
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
|
|
||||||
class SongsFragment : Fragment() {
|
class SongsFragment : Fragment() {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels {
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
PlaybackViewModel.Factory(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
<string name="label_play">Play</string>
|
<string name="label_play">Play</string>
|
||||||
<string name="label_queue">Queue</string>
|
<string name="label_queue">Queue</string>
|
||||||
<string name="label_queue_add">Add to queue</string>
|
<string name="label_queue_add">Add to queue</string>
|
||||||
|
<string name="label_notif_playback">Music Playback</string>
|
||||||
|
<string name="label_service_playback">The music playback service for Auxio.</string>
|
||||||
|
<string name="label_is_playing">Auxio is playing music</string>
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">Search Library…</string>
|
<string name="hint_search_library">Search Library…</string>
|
||||||
|
@ -47,9 +50,6 @@
|
||||||
<string name="description_skip_prev">Skip to last song</string>
|
<string name="description_skip_prev">Skip to last song</string>
|
||||||
<string name="description_shuffle_on">Turn shuffle on</string>
|
<string name="description_shuffle_on">Turn shuffle on</string>
|
||||||
<string name="description_shuffle_off">Turn shuffle off</string>
|
<string name="description_shuffle_off">Turn shuffle off</string>
|
||||||
<string name="description_service_playback">The music playback service for Auxio</string>
|
|
||||||
<string name="description_notif_playback">Music Playback</string>
|
|
||||||
<string name="description_playback">Auxio is playing music</string>
|
|
||||||
|
|
||||||
<!-- Placeholder Namespace | Placeholder values -->
|
<!-- Placeholder Namespace | Placeholder values -->
|
||||||
<string name="placeholder_genre">Unknown Genre</string>
|
<string name="placeholder_genre">Unknown Genre</string>
|
||||||
|
|
Loading…
Reference in a new issue