Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • bugfix/46-adjust-now-playing-top-margin
  • deploy-in-docker
  • develop
  • enhancement/speed-up-pipelines
  • fix-ci-for-tags
  • gradle-v8
  • housekeeping/mavenCentral
  • housekeeping/remove-warnings
  • renovate/io.mockk-mockk-1.x
  • technical/skip-metadata-file-for-branches
  • technical/update-compile-sdk-version-docker
  • technical/update-jvmTarget-version
  • technical/upgrade-appcompat-1.5.x
  • technical/upgrade-exoplayer
  • 0.0.1
  • 0.1
  • 0.1.1
  • 0.1.2
  • 0.1.3
  • 0.1.4
  • 0.1.5
  • 0.1.5-1
  • 0.2.0
  • 0.2.1
  • 0.2.1-1
  • 0.3.0
  • fdroid-dummy-1
  • fdroid-dummy-2
  • fdroid-dummy-3
  • fdroid-dummy-4
  • fdroid-dummy-5
31 results

Target

Select target project
  • funkwhale/funkwhale-android
  • creak/funkwhale-android
  • Keunes/funkwhale-android
  • Mouath/funkwhale-android
4 results
Select Git revision
  • bugfix/46-adjust-now-playing-top-margin
  • bugfix/90-error-playing-user-radio
  • deploy-in-docker
  • develop
  • enhancement/89-update-appstore-metadata
  • enhancement/speed-up-pipelines
  • housekeeping/remove-warnings
  • renovate/configure
  • 0.0.1
  • 0.1
  • 0.1.1
11 results
Show changes
Showing
with 257 additions and 86 deletions
...@@ -9,12 +9,12 @@ import androidx.lifecycle.lifecycleScope ...@@ -9,12 +9,12 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.adapters.RadiosAdapter import audio.funkwhale.ffa.adapters.RadiosAdapter
import audio.funkwhale.ffa.databinding.FragmentRadiosBinding import audio.funkwhale.ffa.databinding.FragmentRadiosBinding
import audio.funkwhale.ffa.model.Radio
import audio.funkwhale.ffa.repositories.RadiosRepository import audio.funkwhale.ffa.repositories.RadiosRepository
import audio.funkwhale.ffa.utils.Command import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.Event import audio.funkwhale.ffa.utils.Event
import audio.funkwhale.ffa.utils.EventBus import audio.funkwhale.ffa.utils.EventBus
import audio.funkwhale.ffa.model.Radio
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
...@@ -62,8 +62,7 @@ class RadiosFragment : FFAFragment<Radio, RadiosAdapter>() { ...@@ -62,8 +62,7 @@ class RadiosFragment : FFAFragment<Radio, RadiosAdapter>() {
lifecycleScope.launch(Main) { lifecycleScope.launch(Main) {
EventBus.get().collect { message -> EventBus.get().collect { message ->
when (message) { if (message is Event.RadioStarted) {
is Event.RadioStarted ->
recycler.forEach { recycler.forEach {
it.isEnabled = true it.isEnabled = true
it.isClickable = true it.isClickable = true
......
package audio.funkwhale.ffa.activities package audio.funkwhale.ffa.fragments
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import audio.funkwhale.ffa.adapters.FavoriteListener
import audio.funkwhale.ffa.adapters.SearchAdapter import audio.funkwhale.ffa.adapters.SearchAdapter
import audio.funkwhale.ffa.databinding.ActivitySearchBinding import audio.funkwhale.ffa.databinding.FragmentSearchBinding
import audio.funkwhale.ffa.fragments.AddToPlaylistDialog
import audio.funkwhale.ffa.fragments.AlbumsFragment
import audio.funkwhale.ffa.fragments.ArtistsFragment
import audio.funkwhale.ffa.model.Album import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.model.Artist import audio.funkwhale.ffa.model.Artist
import audio.funkwhale.ffa.repositories.* import audio.funkwhale.ffa.repositories.FavoritesRepository
import audio.funkwhale.ffa.utils.* import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.Event
import audio.funkwhale.ffa.utils.EventBus
import audio.funkwhale.ffa.utils.getMetadata
import audio.funkwhale.ffa.viewmodel.SearchViewModel
import com.google.android.exoplayer2.offline.Download import com.google.android.exoplayer2.offline.Download
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.URLEncoder
import java.util.*
class SearchActivity : AppCompatActivity() { class SearchFragment : Fragment() {
private lateinit var adapter: SearchAdapter private lateinit var adapter: SearchAdapter
private lateinit var binding: FragmentSearchBinding
private lateinit var artistsRepository: ArtistsSearchRepository private val viewModel by activityViewModels<SearchViewModel>()
private lateinit var albumsRepository: AlbumsSearchRepository private val noSearchYet = MutableLiveData(true)
private lateinit var tracksRepository: TracksSearchRepository
private lateinit var favoritesRepository: FavoritesRepository override fun onCreateView(
private lateinit var binding: ActivitySearchBinding inflater: LayoutInflater,
container: ViewGroup?,
var done = 0 savedInstanceState: Bundle?
): View {
override fun onCreate(savedInstanceState: Bundle?) { binding = FragmentSearchBinding.inflate(layoutInflater, container, false)
super.onCreate(savedInstanceState) binding.lifecycleOwner = this
binding.isLoadingData = viewModel.isLoadingData
binding = ActivitySearchBinding.inflate(layoutInflater) binding.hasResults = viewModel.hasResults
binding.noSearchYet = noSearchYet
setContentView(binding.root) return binding.root
adapter =
SearchAdapter(layoutInflater, this, SearchResultClickListener(), FavoriteListener()).also {
binding.results.layoutManager = LinearLayoutManager(this)
binding.results.adapter = it
}
binding.search.requestFocus()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
binding.search.requestFocus()
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
CommandBus.get().collect { command -> CommandBus.get().collect { command ->
when (command) { if (command is Command.AddToPlaylist) {
is Command.AddToPlaylist -> if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
AddToPlaylistDialog.show( AddToPlaylistDialog.show(
layoutInflater, layoutInflater,
this@SearchActivity, requireActivity(),
lifecycleScope, lifecycleScope,
command.tracks command.tracks
) )
...@@ -69,85 +69,40 @@ class SearchActivity : AppCompatActivity() { ...@@ -69,85 +69,40 @@ class SearchActivity : AppCompatActivity() {
} }
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
EventBus.get().collect { message -> EventBus.get().collect { event ->
when (message) { if (event is Event.DownloadChanged) refreshDownloadedTrack(event.download)
is Event.DownloadChanged -> refreshDownloadedTrack(message.download)
} }
} }
}
artistsRepository = ArtistsSearchRepository(this@SearchActivity, "")
albumsRepository = AlbumsSearchRepository(this@SearchActivity, "")
tracksRepository = TracksSearchRepository(this@SearchActivity, "")
favoritesRepository = FavoritesRepository(this@SearchActivity)
binding.search.setOnQueryTextListener(object :
androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(rawQuery: String?): Boolean {
binding.search.clearFocus()
rawQuery?.let {
done = 0
val query = URLEncoder.encode(it, "UTF-8")
artistsRepository.query = query.lowercase(Locale.ROOT)
albumsRepository.query = query.lowercase(Locale.ROOT)
tracksRepository.query = query.lowercase(Locale.ROOT)
binding.searchSpinner.visibility = View.VISIBLE adapter =
binding.searchEmpty.visibility = View.GONE SearchAdapter(
binding.searchNoResults.visibility = View.GONE viewModel,
this,
adapter.artists.clear() SearchResultClickListener(),
adapter.albums.clear() FavoriteListener(FavoritesRepository(requireContext()))
adapter.tracks.clear() ).also {
adapter.notifyDataSetChanged() binding.results.layoutManager = LinearLayoutManager(requireContext())
binding.results.adapter = it
artistsRepository.fetch(Repository.Origin.Network.origin)
.untilNetwork(lifecycleScope) { artists, _, _, _ ->
done++
adapter.artists.addAll(artists)
refresh()
}
albumsRepository.fetch(Repository.Origin.Network.origin)
.untilNetwork(lifecycleScope) { albums, _, _, _ ->
done++
adapter.albums.addAll(albums)
refresh()
} }
tracksRepository.fetch(Repository.Origin.Network.origin) binding.search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
.untilNetwork(lifecycleScope) { tracks, _, _, _ ->
done++
adapter.tracks.addAll(tracks) override fun onQueryTextSubmit(query: String): Boolean {
refresh() binding.search.clearFocus()
} noSearchYet.value = false
} viewModel.query.postValue(query)
return true return true
} }
override fun onQueryTextChange(newText: String?) = true override fun onQueryTextChange(newText: String) = true
}) })
} }
private fun refresh() { override fun onDestroy() {
adapter.notifyDataSetChanged() super.onDestroy()
// Empty the research to prevent result recall the next time
if (adapter.artists.size + adapter.albums.size + adapter.tracks.size == 0) { viewModel.query.value = ""
binding.searchNoResults.visibility = View.VISIBLE
} else {
binding.searchNoResults.visibility = View.GONE
}
if (done == 3) {
binding.searchSpinner.visibility = View.INVISIBLE
}
} }
private suspend fun refreshDownloadedTrack(download: Download) { private suspend fun refreshDownloadedTrack(download: Download) {
...@@ -171,20 +126,11 @@ class SearchActivity : AppCompatActivity() { ...@@ -171,20 +126,11 @@ class SearchActivity : AppCompatActivity() {
inner class SearchResultClickListener : SearchAdapter.OnSearchResultClickListener { inner class SearchResultClickListener : SearchAdapter.OnSearchResultClickListener {
override fun onArtistClick(holder: View?, artist: Artist) { override fun onArtistClick(holder: View?, artist: Artist) {
ArtistsFragment.openAlbums(this@SearchActivity, artist) findNavController().navigate(SearchFragmentDirections.searchToAlbums(artist))
} }
override fun onAlbumClick(holder: View?, album: Album) { override fun onAlbumClick(holder: View?, album: Album) {
AlbumsFragment.openTracks(this@SearchActivity, album) findNavController().navigate(SearchFragmentDirections.searchToTracks(album))
}
}
inner class FavoriteListener : SearchAdapter.OnFavoriteListener {
override fun onToggleFavorite(id: Int, state: Boolean) {
when (state) {
true -> favoritesRepository.addFavorite(id)
false -> favoritesRepository.deleteFavorite(id)
}
} }
} }
} }
...@@ -8,32 +8,41 @@ import android.view.LayoutInflater ...@@ -8,32 +8,41 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.adapters.FavoriteListener
import audio.funkwhale.ffa.adapters.TracksAdapter import audio.funkwhale.ffa.adapters.TracksAdapter
import audio.funkwhale.ffa.databinding.FragmentTracksBinding import audio.funkwhale.ffa.databinding.FragmentTracksBinding
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.model.Track import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.repositories.FavoritedRepository import audio.funkwhale.ffa.repositories.FavoritedRepository
import audio.funkwhale.ffa.repositories.FavoritesRepository import audio.funkwhale.ffa.repositories.FavoritesRepository
import audio.funkwhale.ffa.repositories.TracksRepository import audio.funkwhale.ffa.repositories.TracksRepository
import audio.funkwhale.ffa.utils.* import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.CoverArt
import audio.funkwhale.ffa.utils.Event
import audio.funkwhale.ffa.utils.EventBus
import audio.funkwhale.ffa.utils.Request
import audio.funkwhale.ffa.utils.RequestBus
import audio.funkwhale.ffa.utils.Response
import audio.funkwhale.ffa.utils.getMetadata
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import audio.funkwhale.ffa.utils.toast
import audio.funkwhale.ffa.utils.wait
import com.google.android.exoplayer2.offline.Download import com.google.android.exoplayer2.offline.Download
import com.google.android.exoplayer2.offline.DownloadManager import com.google.android.exoplayer2.offline.DownloadManager
import com.preference.PowerPreference import com.preference.PowerPreference
import com.squareup.picasso.Picasso
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.java.KoinJavaComponent.inject import org.koin.java.KoinJavaComponent.inject
class TracksFragment : FFAFragment<Track, TracksAdapter>() { class TracksFragment : FFAFragment<Track, TracksAdapter>() {
private val args by navArgs<TracksFragmentArgs>()
private val exoDownloadManager: DownloadManager by inject(DownloadManager::class.java) private val exoDownloadManager: DownloadManager by inject(DownloadManager::class.java)
override val recycler: RecyclerView get() = binding.tracks override val recycler: RecyclerView get() = binding.tracks
...@@ -44,38 +53,14 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -44,38 +53,14 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
private lateinit var favoritesRepository: FavoritesRepository private lateinit var favoritesRepository: FavoritesRepository
private lateinit var favoritedRepository: FavoritedRepository private lateinit var favoritedRepository: FavoritedRepository
private var albumId = 0
private var albumArtist = ""
private var albumTitle = ""
private var albumCover = ""
companion object {
fun new(album: Album): TracksFragment {
return TracksFragment().apply {
arguments = bundleOf(
"albumId" to album.id,
"albumArtist" to album.artist.name,
"albumTitle" to album.title,
"albumCover" to album.cover()
)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.apply {
albumId = getInt("albumId")
albumArtist = getString("albumArtist") ?: ""
albumTitle = getString("albumTitle") ?: ""
albumCover = getString("albumCover") ?: ""
}
adapter = TracksAdapter(layoutInflater, context, FavoriteListener())
repository = TracksRepository(context, albumId)
favoritesRepository = FavoritesRepository(context) favoritesRepository = FavoritesRepository(context)
favoritedRepository = FavoritedRepository(context) favoritedRepository = FavoritedRepository(context)
repository = TracksRepository(context, args.album.id)
adapter = TracksAdapter(layoutInflater, context, FavoriteListener(favoritesRepository))
watchEventBus() watchEventBus()
} }
...@@ -116,6 +101,12 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -116,6 +101,12 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
): View { ): View {
_binding = FragmentTracksBinding.inflate(inflater) _binding = FragmentTracksBinding.inflate(inflater)
swiper = binding.swiper swiper = binding.swiper
when (PowerPreference.getDefaultFile().getString("play_order")) {
"in_order" -> binding.play.text = getString(R.string.playback_play)
else -> binding.play.text = getString(R.string.playback_shuffle)
}
return binding.root return binding.root
} }
...@@ -127,16 +118,15 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -127,16 +118,15 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
Picasso.get() CoverArt.requestCreator(maybeNormalizeUrl(args.album.cover()))
.maybeLoad(maybeNormalizeUrl(albumCover))
.noFade() .noFade()
.fit() .fit()
.centerCrop() .centerCrop()
.transform(RoundedCornersTransformation(16, 0)) .transform(RoundedCornersTransformation(16, 0))
.into(binding.cover) .into(binding.cover)
binding.artist.text = albumArtist binding.artist.text = args.album.artist.name
binding.title.text = albumTitle binding.title.text = args.album.title
} }
override fun onResume() { override fun onResume() {
...@@ -175,7 +165,6 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -175,7 +165,6 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
"in_order" -> CommandBus.send(Command.ReplaceQueue(adapter.data)) "in_order" -> CommandBus.send(Command.ReplaceQueue(adapter.data))
else -> CommandBus.send(Command.ReplaceQueue(adapter.data.shuffled())) else -> CommandBus.send(Command.ReplaceQueue(adapter.data.shuffled()))
} }
context.toast("All tracks were added to your queue") context.toast("All tracks were added to your queue")
} }
...@@ -199,8 +188,10 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -199,8 +188,10 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
setOnMenuItemClickListener { setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.play_secondary -> when (PowerPreference.getDefaultFile() R.id.play_secondary -> when (
.getString("play_order")) { PowerPreference.getDefaultFile()
.getString("play_order")
) {
"in_order" -> CommandBus.send(Command.ReplaceQueue(adapter.data.shuffled())) "in_order" -> CommandBus.send(Command.ReplaceQueue(adapter.data.shuffled()))
else -> CommandBus.send(Command.ReplaceQueue(adapter.data)) else -> CommandBus.send(Command.ReplaceQueue(adapter.data))
} }
...@@ -229,16 +220,16 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -229,16 +220,16 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
private fun watchEventBus() { private fun watchEventBus() {
lifecycleScope.launch(IO) { lifecycleScope.launch(IO) {
EventBus.get().collect { message -> EventBus.get().collect { message ->
when (message) { if (message is Event.DownloadChanged) {
is Event.DownloadChanged -> refreshDownloadedTrack(message.download) refreshDownloadedTrack(message.download)
} }
} }
} }
lifecycleScope.launch(Main) { lifecycleScope.launch(Main) {
CommandBus.get().collect { command -> CommandBus.get().collect { command ->
when (command) { if (command is Command.RefreshTrack) {
is Command.RefreshTrack -> refreshCurrentTrack(command.track) refreshCurrentTrack(command.track)
} }
} }
} }
...@@ -248,10 +239,12 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -248,10 +239,12 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
val downloaded = TracksRepository.getDownloadedIds(exoDownloadManager) ?: listOf() val downloaded = TracksRepository.getDownloadedIds(exoDownloadManager) ?: listOf()
withContext(Main) { withContext(Main) {
adapter.data = adapter.data.map { adapter.setUnfilteredData(
adapter.data.map {
it.downloaded = downloaded.contains(it.id) it.downloaded = downloaded.contains(it.id)
it it
}.toMutableList() }.toMutableList()
)
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
...@@ -281,13 +274,4 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() { ...@@ -281,13 +274,4 @@ class TracksFragment : FFAFragment<Track, TracksAdapter>() {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
} }
inner class FavoriteListener : TracksAdapter.OnFavoriteListener {
override fun onToggleFavorite(id: Int, state: Boolean) {
when (state) {
true -> favoritesRepository.addFavorite(id)
false -> favoritesRepository.deleteFavorite(id)
}
}
}
} }
...@@ -6,7 +6,7 @@ import audio.funkwhale.ffa.playback.MediaSession ...@@ -6,7 +6,7 @@ import audio.funkwhale.ffa.playback.MediaSession
import audio.funkwhale.ffa.utils.AuthorizationServiceFactory import audio.funkwhale.ffa.utils.AuthorizationServiceFactory
import audio.funkwhale.ffa.utils.OAuth import audio.funkwhale.ffa.utils.OAuth
import com.google.android.exoplayer2.database.DatabaseProvider import com.google.android.exoplayer2.database.DatabaseProvider
import com.google.android.exoplayer2.database.ExoDatabaseProvider import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
import com.google.android.exoplayer2.offline.DownloadManager import com.google.android.exoplayer2.offline.DownloadManager
import com.google.android.exoplayer2.upstream.cache.Cache import com.google.android.exoplayer2.upstream.cache.Cache
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
...@@ -19,7 +19,7 @@ import org.koin.dsl.module ...@@ -19,7 +19,7 @@ import org.koin.dsl.module
fun exoplayerModule(context: Context) = module { fun exoplayerModule(context: Context) = module {
single<DatabaseProvider>(named("exoDatabase")) { single<DatabaseProvider>(named("exoDatabase")) {
ExoDatabaseProvider(context) StandaloneDatabaseProvider(context)
} }
single { single {
......
package audio.funkwhale.ffa.model package audio.funkwhale.ffa.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class Album( data class Album(
val id: Int, val id: Int,
val artist: Artist, val artist: Artist,
val title: String, val title: String,
val cover: Covers?, private val cover: Covers?,
val release_date: String? val release_date: String?
) : SearchResult { ) : SearchResult, Parcelable {
data class Artist(val name: String) @Parcelize
data class Artist(val name: String) : Parcelable
override fun cover() = cover?.urls?.original override fun cover() = cover?.urls?.original
override fun title() = title override fun title() = title
......
package audio.funkwhale.ffa.model package audio.funkwhale.ffa.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import java.util.Calendar.DAY_OF_YEAR
import java.util.GregorianCalendar
@Parcelize
data class Artist( data class Artist(
val id: Int, val id: Int,
val name: String, val name: String,
val albums: List<Album>? val albums: List<Album>?
) : SearchResult { ) : SearchResult, Parcelable {
@Parcelize
data class Album( data class Album(
val title: String, val title: String,
val cover: Covers? val cover: Covers?
) ) : Parcelable
override fun cover(): String? = albums?.mapNotNull { it.cover?.urls?.original }?.let { covers ->
if (covers.isEmpty()) {
return@let null
}
// Inject a little whimsy: rotate through the album covers daily
val index = GregorianCalendar().get(DAY_OF_YEAR) % covers.size
covers.getOrNull(index)
}
override fun cover(): String? = albums?.getOrNull(0)?.cover?.urls?.original
override fun title() = name override fun title() = name
override fun subtitle() = "Artist" override fun subtitle() = "Artist"
} }
...@@ -5,6 +5,7 @@ sealed class CacheItem<D : Any>(val data: List<D>) ...@@ -5,6 +5,7 @@ sealed class CacheItem<D : Any>(val data: List<D>)
class ArtistsCache(data: List<Artist>) : CacheItem<Artist>(data) class ArtistsCache(data: List<Artist>) : CacheItem<Artist>(data)
class AlbumsCache(data: List<Album>) : CacheItem<Album>(data) class AlbumsCache(data: List<Album>) : CacheItem<Album>(data)
class TracksCache(data: List<Track>) : CacheItem<Track>(data) class TracksCache(data: List<Track>) : CacheItem<Track>(data)
class FavoritesCache(data: List<Favorite>) : CacheItem<Favorite>(data)
class PlaylistsCache(data: List<Playlist>) : CacheItem<Playlist>(data) class PlaylistsCache(data: List<Playlist>) : CacheItem<Playlist>(data)
class PlaylistTracksCache(data: List<PlaylistTrack>) : CacheItem<PlaylistTrack>(data) class PlaylistTracksCache(data: List<PlaylistTrack>) : CacheItem<PlaylistTrack>(data)
class RadiosCache(data: List<Radio>) : CacheItem<Radio>(data) class RadiosCache(data: List<Radio>) : CacheItem<Radio>(data)
......
package audio.funkwhale.ffa.model package audio.funkwhale.ffa.model
data class CoverUrls(val original: String) import android.os.Parcelable
\ No newline at end of file import kotlinx.parcelize.Parcelize
@Parcelize
data class CoverUrls(val original: String) : Parcelable
package audio.funkwhale.ffa.model package audio.funkwhale.ffa.model
data class Covers(val urls: CoverUrls) import android.os.Parcelable
\ No newline at end of file import kotlinx.parcelize.Parcelize
@Parcelize
data class Covers(val urls: CoverUrls) : Parcelable
package audio.funkwhale.ffa.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class Favorite(
val id: Int = 0,
val track: Track
) : Parcelable
package audio.funkwhale.ffa.model
data class FavoritesResponse(
override val count: Int,
override val next: String?,
val results: List<Favorite>
) : FFAResponse<Favorite>() {
override fun getData() = results
}
package audio.funkwhale.ffa.model package audio.funkwhale.ffa.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class Playlist( data class Playlist(
val id: Int, val id: Int,
val name: String, val name: String,
val album_covers: List<String>, val album_covers: List<String>,
val tracks_count: Int, val tracks_count: Int,
val duration: Int val duration: Int
) ) : Parcelable
\ No newline at end of file