Merge branch 'housekeeping/upgrade-android-gradle-plugin-and-cleanup' into 'develop'

Upgrading Android Gradle Plugin

See merge request funkwhale/funkwhale-android!72
This commit is contained in:
Ryan Harg 2021-08-22 07:56:09 +00:00
commit 5125098ea4
69 changed files with 344 additions and 279 deletions

View File

@ -25,6 +25,7 @@ import audio.funkwhale.ffa.FFA
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.ActivityMainBinding
import audio.funkwhale.ffa.fragments.*
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.playback.MediaControlsManager
import audio.funkwhale.ffa.playback.PinService
import audio.funkwhale.ffa.playback.PlayerService

View File

@ -11,6 +11,8 @@ import audio.funkwhale.ffa.databinding.ActivitySearchBinding
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.Artist
import audio.funkwhale.ffa.repositories.*
import audio.funkwhale.ffa.utils.*
import com.google.android.exoplayer2.offline.Download

View File

@ -7,7 +7,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.databinding.RowAlbumBinding
import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.Album
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import com.squareup.picasso.Picasso

View File

@ -7,7 +7,7 @@ import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.RowAlbumGridBinding
import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.Album
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import com.squareup.picasso.Picasso

View File

@ -8,7 +8,7 @@ import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.RowArtistBinding
import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.Artist
import audio.funkwhale.ffa.model.Artist
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import com.squareup.picasso.Picasso

View File

@ -9,8 +9,8 @@ import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.RowDownloadBinding
import audio.funkwhale.ffa.playback.PinService
import audio.funkwhale.ffa.utils.DownloadInfo
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.DownloadInfo
import audio.funkwhale.ffa.model.Track
import com.google.android.exoplayer2.offline.Download
import com.google.android.exoplayer2.offline.DownloadService

View File

@ -15,7 +15,7 @@ import audio.funkwhale.ffa.databinding.RowTrackBinding
import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import audio.funkwhale.ffa.utils.toast

View File

@ -18,8 +18,8 @@ import audio.funkwhale.ffa.databinding.RowTrackBinding
import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.PlaylistTrack
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.PlaylistTrack
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import audio.funkwhale.ffa.utils.toast

View File

@ -9,7 +9,7 @@ import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.RowPlaylistBinding
import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.Playlist
import audio.funkwhale.ffa.model.Playlist
import audio.funkwhale.ffa.utils.toDurationString
import com.squareup.picasso.Picasso
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation

View File

@ -12,7 +12,7 @@ import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.AppContext
import audio.funkwhale.ffa.utils.Event
import audio.funkwhale.ffa.utils.EventBus
import audio.funkwhale.ffa.utils.Radio
import audio.funkwhale.ffa.model.Radio
import audio.funkwhale.ffa.views.LoadingImageView
import com.preference.PowerPreference
import kotlinx.coroutines.CoroutineScope

View File

@ -15,11 +15,11 @@ import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.RowSearchHeaderBinding
import audio.funkwhale.ffa.databinding.RowTrackBinding
import audio.funkwhale.ffa.utils.Album
import audio.funkwhale.ffa.utils.Artist
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.model.Artist
import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import audio.funkwhale.ffa.utils.onApi

View File

@ -20,7 +20,7 @@ import audio.funkwhale.ffa.databinding.RowTrackBinding
import audio.funkwhale.ffa.fragments.FFAAdapter
import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import audio.funkwhale.ffa.utils.toast

View File

@ -11,6 +11,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.adapters.PlaylistsAdapter
import audio.funkwhale.ffa.databinding.DialogAddToPlaylistBinding
import audio.funkwhale.ffa.model.Playlist
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.repositories.ManagementPlaylistsRepository
import audio.funkwhale.ffa.utils.*
import com.google.gson.Gson

View File

@ -20,6 +20,8 @@ import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.activities.MainActivity
import audio.funkwhale.ffa.adapters.AlbumsAdapter
import audio.funkwhale.ffa.databinding.FragmentAlbumsBinding
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.model.Artist
import audio.funkwhale.ffa.repositories.AlbumsRepository
import audio.funkwhale.ffa.repositories.ArtistTracksRepository
import audio.funkwhale.ffa.repositories.Repository

View File

@ -14,7 +14,7 @@ import audio.funkwhale.ffa.activities.MainActivity
import audio.funkwhale.ffa.adapters.AlbumsGridAdapter
import audio.funkwhale.ffa.databinding.FragmentAlbumsGridBinding
import audio.funkwhale.ffa.repositories.AlbumsRepository
import audio.funkwhale.ffa.utils.Album
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.utils.AppContext
class AlbumsGridFragment : FFAFragment<Album, AlbumsGridAdapter>() {

View File

@ -15,9 +15,9 @@ import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.activities.MainActivity
import audio.funkwhale.ffa.adapters.ArtistsAdapter
import audio.funkwhale.ffa.databinding.FragmentArtistsBinding
import audio.funkwhale.ffa.model.Artist
import audio.funkwhale.ffa.repositories.ArtistsRepository
import audio.funkwhale.ffa.utils.AppContext
import audio.funkwhale.ffa.utils.Artist
import audio.funkwhale.ffa.utils.onViewPager
class ArtistsFragment : FFAFragment<Artist, ArtistsAdapter>() {

View File

@ -8,6 +8,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.adapters.FavoritesAdapter
import audio.funkwhale.ffa.databinding.FragmentFavoritesBinding
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.repositories.FavoritesRepository
import audio.funkwhale.ffa.repositories.TracksRepository
import audio.funkwhale.ffa.utils.*

View File

@ -17,12 +17,12 @@ import audio.funkwhale.ffa.repositories.ManagementPlaylistsRepository
import audio.funkwhale.ffa.repositories.PlaylistTracksRepository
import audio.funkwhale.ffa.utils.Command
import audio.funkwhale.ffa.utils.CommandBus
import audio.funkwhale.ffa.utils.Playlist
import audio.funkwhale.ffa.utils.PlaylistTrack
import audio.funkwhale.ffa.model.Playlist
import audio.funkwhale.ffa.model.PlaylistTrack
import audio.funkwhale.ffa.utils.Request
import audio.funkwhale.ffa.utils.RequestBus
import audio.funkwhale.ffa.utils.Response
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.maybeLoad
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import audio.funkwhale.ffa.utils.toast

View File

@ -14,7 +14,7 @@ import audio.funkwhale.ffa.adapters.PlaylistsAdapter
import audio.funkwhale.ffa.databinding.FragmentPlaylistsBinding
import audio.funkwhale.ffa.repositories.PlaylistsRepository
import audio.funkwhale.ffa.utils.AppContext
import audio.funkwhale.ffa.utils.Playlist
import audio.funkwhale.ffa.model.Playlist
class PlaylistsFragment : FFAFragment<Playlist, PlaylistsAdapter>() {

View File

@ -14,7 +14,7 @@ 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.Radio
import audio.funkwhale.ffa.model.Radio
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

View File

@ -12,7 +12,7 @@ import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.databinding.FragmentTrackInfoDetailsBinding
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.mustNormalizeUrl
import audio.funkwhale.ffa.utils.toDurationString

View File

@ -14,6 +14,8 @@ import androidx.recyclerview.widget.RecyclerView
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.adapters.TracksAdapter
import audio.funkwhale.ffa.databinding.FragmentTracksBinding
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.repositories.FavoritedRepository
import audio.funkwhale.ffa.repositories.FavoritesRepository
import audio.funkwhale.ffa.repositories.TracksRepository

View File

@ -0,0 +1,17 @@
package audio.funkwhale.ffa.model
data class Album(
val id: Int,
val artist: Artist,
val title: String,
val cover: Covers?,
val release_date: String?
) : SearchResult {
data class Artist(val name: String)
override fun cover() = cover?.urls?.original
override fun title() = title
override fun subtitle() = artist.name
}
typealias AlbumList = List<Album>

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class AlbumsResponse(
override val count: Int,
override val next: String?,
val results: AlbumList
) : FFAResponse<Album>() {
override fun getData() = results
}

View File

@ -0,0 +1,16 @@
package audio.funkwhale.ffa.model
data class Artist(
val id: Int,
val name: String,
val albums: List<Album>?
) : SearchResult {
data class Album(
val title: String,
val cover: Covers?
)
override fun cover(): String? = albums?.getOrNull(0)?.cover?.urls?.original
override fun title() = name
override fun subtitle() = "Artist"
}

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class ArtistsResponse(
override val count: Int,
override val next: String?,
val results: List<Artist>
) : FFAResponse<Artist>() {
override fun getData() = results
}

View File

@ -0,0 +1,12 @@
package audio.funkwhale.ffa.model
sealed class CacheItem<D : Any>(val data: List<D>)
class ArtistsCache(data: List<Artist>) : CacheItem<Artist>(data)
class AlbumsCache(data: List<Album>) : CacheItem<Album>(data)
class TracksCache(data: List<Track>) : CacheItem<Track>(data)
class PlaylistsCache(data: List<Playlist>) : CacheItem<Playlist>(data)
class PlaylistTracksCache(data: List<PlaylistTrack>) : CacheItem<PlaylistTrack>(data)
class RadiosCache(data: List<Radio>) : CacheItem<Radio>(data)
class FavoritedCache(data: List<Int>) : CacheItem<Int>(data)
class QueueCache(data: List<Track>) : CacheItem<Track>(data)

View File

@ -0,0 +1,3 @@
package audio.funkwhale.ffa.model
data class CoverUrls(val original: String)

View File

@ -0,0 +1,3 @@
package audio.funkwhale.ffa.model
data class Covers(val urls: CoverUrls)

View File

@ -0,0 +1,11 @@
package audio.funkwhale.ffa.model
import com.google.android.exoplayer2.offline.Download
data class DownloadInfo(
val id: Int,
val contentId: String,
val title: String,
val artist: String,
var download: Download?
)

View File

@ -0,0 +1,8 @@
package audio.funkwhale.ffa.model
abstract class FFAResponse<D : Any> {
abstract val count: Int
abstract val next: String?
abstract fun getData(): List<D>
}

View File

@ -0,0 +1,3 @@
package audio.funkwhale.ffa.model
data class Favorited(val track: Int)

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class FavoritedResponse(
override val count: Int,
override val next: String?,
val results: List<Favorited>
) : FFAResponse<Int>() {
override fun getData() = results.map { it.track }
}

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class Playlist(
val id: Int,
val name: String,
val album_covers: List<String>,
val tracks_count: Int,
val duration: Int
)

View File

@ -0,0 +1,3 @@
package audio.funkwhale.ffa.model
data class PlaylistTrack(val track: Track)

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class PlaylistTracksResponse(
override val count: Int,
override val next: String?,
val results: List<PlaylistTrack>
) : FFAResponse<PlaylistTrack>() {
override fun getData() = results
}

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class PlaylistsResponse(
override val count: Int,
override val next: String?,
val results: List<Playlist>
) : FFAResponse<Playlist>() {
override fun getData() = results
}

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class Radio(
val id: Int,
var radio_type: String,
val name: String,
val description: String,
var related_object_id: String? = null
)

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class RadiosResponse(
override val count: Int,
override val next: String?,
val results: List<Radio>
) : FFAResponse<Radio>() {
override fun getData() = results
}

View File

@ -0,0 +1,7 @@
package audio.funkwhale.ffa.model
interface SearchResult {
fun cover(): String?
fun title(): String
fun subtitle(): String
}

View File

@ -0,0 +1,64 @@
package audio.funkwhale.ffa.model
import com.preference.PowerPreference
data class Track(
val id: Int = 0,
val title: String,
val artist: Artist,
val album: Album?,
val disc_number: Int = 0,
val position: Int = 0,
val uploads: List<Upload> = listOf(),
val copyright: String? = null,
val license: String? = null
) : SearchResult {
var current: Boolean = false
var favorite: Boolean = false
var cached: Boolean = false
var downloaded: Boolean = false
companion object {
fun fromDownload(download: DownloadInfo): Track = Track(
id = download.id,
title = download.title,
artist = Artist(0, download.artist, listOf()),
album = Album(0, Album.Artist(""), "", Covers(CoverUrls("")), ""),
uploads = listOf(Upload(download.contentId, 0, 0))
)
}
data class Upload(
val listen_url: String,
val duration: Int,
val bitrate: Int
)
override fun equals(other: Any?): Boolean {
return when (other) {
is Track -> other.id == id
else -> false
}
}
override fun hashCode(): Int {
return id
}
fun bestUpload(): Upload? {
if (uploads.isEmpty()) return null
return when (PowerPreference.getDefaultFile().getString("media_cache_quality")) {
"quality" -> uploads.maxByOrNull { it.bitrate } ?: uploads[0]
"size" -> uploads.minByOrNull { it.bitrate } ?: uploads[0]
else -> uploads.maxByOrNull { it.bitrate } ?: uploads[0]
}
}
override fun cover() = album?.cover?.urls?.original
override fun title() = title
override fun subtitle() = artist.name
val formatted: String get() = "$id $artist ($album): $title"
}

View File

@ -0,0 +1,9 @@
package audio.funkwhale.ffa.model
data class TracksResponse(
override val count: Int,
override val next: String?,
val results: List<Track>
) : FFAResponse<Track>() {
override fun getData() = results
}

View File

@ -0,0 +1,5 @@
package audio.funkwhale.ffa.model
data class User(
val full_username: String
)

View File

@ -13,32 +13,32 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory
import com.google.android.exoplayer2.util.Util
class CacheDataSourceFactoryProvider(
private val oAuth: OAuth,
private val exoCache: Cache,
private val exoDownloadCache: Cache
private val oAuth: OAuth,
private val exoCache: Cache,
private val exoDownloadCache: Cache
) {
fun create(context: Context): CacheDataSourceFactory {
val playbackCache =
CacheDataSourceFactory(exoCache, createDatasourceFactory(context, oAuth))
CacheDataSourceFactory(exoCache, createDatasourceFactory(context, oAuth))
return CacheDataSourceFactory(
exoDownloadCache,
playbackCache,
FileDataSource.Factory(),
null,
CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR,
null
exoDownloadCache,
playbackCache,
FileDataSource.Factory(),
null,
CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR,
null
)
}
private fun createDatasourceFactory(context: Context, oAuth: OAuth): DataSource.Factory {
val http = DefaultHttpDataSourceFactory(
Util.getUserAgent(context, context.getString(R.string.app_name))
Util.getUserAgent(context, context.getString(R.string.app_name))
)
return if (!Settings.isAnonymous()) {
OAuth2DatasourceFactory(context, http, oAuth)
OAuth2DatasourceFactory(context, http, oAuth)
} else {
http
}

View File

@ -13,7 +13,7 @@ import androidx.media.session.MediaButtonReceiver
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.activities.MainActivity
import audio.funkwhale.ffa.utils.AppContext
import audio.funkwhale.ffa.utils.Track
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.maybeNormalizeUrl
import com.squareup.picasso.Picasso
import kotlinx.coroutines.CoroutineScope

View File

@ -40,7 +40,7 @@ class MediaSession(private val context: Context) {
val connector: MediaSessionConnector by lazy {
MediaSessionConnector(session).also {
it.setQueueNavigator(OtterQueueNavigator())
it.setQueueNavigator(FFAQueueNavigator())
it.setMediaButtonEventHandler { _, _, intent ->
if (!active) {
@ -59,7 +59,7 @@ class MediaSession(private val context: Context) {
}
}
class OtterQueueNavigator : MediaSessionConnector.QueueNavigator {
class FFAQueueNavigator : MediaSessionConnector.QueueNavigator {
override fun onSkipToQueueItem(player: Player, controlDispatcher: ControlDispatcher, id: Long) {
CommandBus.send(Command.PlayTrack(id.toInt()))
}

View File

@ -5,6 +5,8 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.model.DownloadInfo
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.*
import com.google.android.exoplayer2.offline.Download
import com.google.android.exoplayer2.offline.DownloadManager

View File

@ -12,11 +12,11 @@ import android.media.MediaMetadata
import android.os.Build
import android.os.IBinder
import android.support.v4.media.MediaMetadataCompat
import android.util.Log
import android.view.KeyEvent
import androidx.core.app.NotificationManagerCompat
import androidx.media.session.MediaButtonReceiver
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.*
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlaybackException

View File

@ -2,6 +2,8 @@ package audio.funkwhale.ffa.playback
import android.content.Context
import android.net.Uri
import audio.funkwhale.ffa.model.QueueCache
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.utils.*
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
@ -48,7 +50,6 @@ class QueueManager(val context: Context) {
)
}
fun replace(tracks: List<Track>) {
tracks.map { it.formatted }.log("Replacing queue with ${tracks.size} tracks")
val factory = cacheDataSourceFactoryProvider.create(context)

View File

@ -2,6 +2,8 @@ package audio.funkwhale.ffa.playback
import android.content.Context
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.model.Radio
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.repositories.FavoritedRepository
import audio.funkwhale.ffa.repositories.Repository
import audio.funkwhale.ffa.utils.*

View File

@ -1,9 +1,9 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.Album
import audio.funkwhale.ffa.utils.AlbumsCache
import audio.funkwhale.ffa.utils.AlbumsResponse
import audio.funkwhale.ffa.model.Album
import audio.funkwhale.ffa.model.AlbumsCache
import audio.funkwhale.ffa.model.AlbumsResponse
import audio.funkwhale.ffa.utils.OAuth
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.gson.reflect.TypeToken

View File

@ -1,7 +1,11 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.*
import audio.funkwhale.ffa.model.FFAResponse
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.model.TracksCache
import audio.funkwhale.ffa.model.TracksResponse
import audio.funkwhale.ffa.utils.OAuth
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.gson.reflect.TypeToken
import org.koin.java.KoinJavaComponent.inject
@ -14,7 +18,7 @@ class ArtistTracksRepository(override val context: Context?, private val artistI
override val cacheId = "tracks-artist-$artistId"
override val upstream = HttpUpstream<Track, OtterResponse<Track>>(
override val upstream = HttpUpstream<Track, FFAResponse<Track>>(
context,
HttpUpstream.Behavior.AtOnce,
"/api/v1/tracks/?playable=true&artist=$artistId",

View File

@ -1,7 +1,11 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.*
import audio.funkwhale.ffa.model.Artist
import audio.funkwhale.ffa.model.ArtistsCache
import audio.funkwhale.ffa.model.ArtistsResponse
import audio.funkwhale.ffa.model.FFAResponse
import audio.funkwhale.ffa.utils.OAuth
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.gson.reflect.TypeToken
import org.koin.java.KoinJavaComponent.inject
@ -13,7 +17,7 @@ class ArtistsRepository(override val context: Context?) : Repository<Artist, Art
override val cacheId = "artists"
override val upstream = HttpUpstream<Artist, OtterResponse<Artist>>(
override val upstream = HttpUpstream<Artist, FFAResponse<Artist>>(
context,
HttpUpstream.Behavior.Progressive,
"/api/v1/artists/?playable=true&ordering=name",

View File

@ -1,6 +1,7 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.model.*
import audio.funkwhale.ffa.utils.*
import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.fuel.coroutines.awaitByteArrayResponseResult
@ -25,7 +26,7 @@ class FavoritesRepository(override val context: Context?) : Repository<Track, Tr
override val cacheId = "favorites.v2"
override val upstream = HttpUpstream<Track, OtterResponse<Track>>(
override val upstream = HttpUpstream<Track, FFAResponse<Track>>(
context!!,
HttpUpstream.Behavior.AtOnce,
"/api/v1/tracks/?favorites=true&playable=true&ordering=title",
@ -106,7 +107,7 @@ class FavoritedRepository(override val context: Context?) : Repository<Int, Favo
private val oAuth: OAuth by inject(OAuth::class.java)
override val cacheId = "favorited"
override val upstream = HttpUpstream<Int, OtterResponse<Int>>(
override val upstream = HttpUpstream<Int, FFAResponse<Int>>(
context,
HttpUpstream.Behavior.Single,
"/api/v1/favorites/tracks/all/?playable=true",

View File

@ -3,6 +3,7 @@ package audio.funkwhale.ffa.repositories
import android.content.Context
import android.net.Uri
import android.util.Log
import audio.funkwhale.ffa.model.FFAResponse
import audio.funkwhale.ffa.utils.*
import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.fuel.core.FuelError
@ -19,7 +20,7 @@ import java.io.Reader
import java.lang.reflect.Type
import kotlin.math.ceil
class HttpUpstream<D : Any, R : OtterResponse<D>>(
class HttpUpstream<D : Any, R : FFAResponse<D>>(
val context: Context?,
val behavior: Behavior,
private val url: String,
@ -78,7 +79,7 @@ class HttpUpstream<D : Any, R : OtterResponse<D>>(
hasMore
)
class GenericDeserializer<T : OtterResponse<*>>(val type: Type) : ResponseDeserializable<T> {
class GenericDeserializer<T : FFAResponse<*>>(val type: Type) : ResponseDeserializable<T> {
override fun deserialize(reader: Reader): T? {
return Gson().fromJson(reader, type)
}

View File

@ -1,7 +1,11 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.*
import audio.funkwhale.ffa.model.FFAResponse
import audio.funkwhale.ffa.model.PlaylistTrack
import audio.funkwhale.ffa.model.PlaylistTracksCache
import audio.funkwhale.ffa.model.PlaylistTracksResponse
import audio.funkwhale.ffa.utils.OAuth
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.flow.map
@ -17,7 +21,7 @@ class PlaylistTracksRepository(override val context: Context?, playlistId: Int)
override val cacheId = "tracks-playlist-$playlistId"
override val upstream = HttpUpstream<PlaylistTrack, OtterResponse<PlaylistTrack>>(
override val upstream = HttpUpstream<PlaylistTrack, FFAResponse<PlaylistTrack>>(
context,
HttpUpstream.Behavior.Single,
"/api/v1/playlists/$playlistId/tracks/?playable=true",

View File

@ -1,7 +1,11 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.*
import audio.funkwhale.ffa.model.*
import audio.funkwhale.ffa.utils.OAuth
import audio.funkwhale.ffa.utils.Settings
import audio.funkwhale.ffa.utils.authorize
import audio.funkwhale.ffa.utils.mustNormalizeUrl
import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.fuel.coroutines.awaitByteArrayResponseResult
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
@ -21,7 +25,7 @@ class PlaylistsRepository(override val context: Context?) : Repository<Playlist,
private val oAuth: OAuth by inject(OAuth::class.java)
override val upstream = HttpUpstream<Playlist, OtterResponse<Playlist>>(
override val upstream = HttpUpstream<Playlist, FFAResponse<Playlist>>(
context!!,
HttpUpstream.Behavior.Progressive,
"/api/v1/playlists/?playable=true&ordering=name",
@ -41,7 +45,7 @@ class ManagementPlaylistsRepository(override val context: Context?) :
override val cacheId = "tracks-playlists-management"
override val upstream = HttpUpstream<Playlist, OtterResponse<Playlist>>(
override val upstream = HttpUpstream<Playlist, FFAResponse<Playlist>>(
context,
HttpUpstream.Behavior.AtOnce,
"/api/v1/playlists/?scope=me&ordering=name",

View File

@ -1,7 +1,11 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.*
import audio.funkwhale.ffa.model.FFAResponse
import audio.funkwhale.ffa.model.Radio
import audio.funkwhale.ffa.model.RadiosCache
import audio.funkwhale.ffa.model.RadiosResponse
import audio.funkwhale.ffa.utils.OAuth
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.gson.reflect.TypeToken
import org.koin.java.KoinJavaComponent.inject
@ -13,7 +17,7 @@ class RadiosRepository(override val context: Context?) : Repository<Radio, Radio
override val cacheId = "radios"
override val upstream = HttpUpstream<Radio, OtterResponse<Radio>>(
override val upstream = HttpUpstream<Radio, FFAResponse<Radio>>(
context,
HttpUpstream.Behavior.Progressive,
"/api/v1/radios/radios/?ordering=name",

View File

@ -2,7 +2,7 @@ package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.AppContext
import audio.funkwhale.ffa.utils.CacheItem
import audio.funkwhale.ffa.model.CacheItem
import audio.funkwhale.ffa.utils.FFACache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO

View File

@ -1,6 +1,7 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.model.*
import audio.funkwhale.ffa.utils.*
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.android.exoplayer2.offline.DownloadManager

View File

@ -1,7 +1,13 @@
package audio.funkwhale.ffa.repositories
import android.content.Context
import audio.funkwhale.ffa.utils.*
import audio.funkwhale.ffa.model.FFAResponse
import audio.funkwhale.ffa.model.Track
import audio.funkwhale.ffa.model.TracksCache
import audio.funkwhale.ffa.model.TracksResponse
import audio.funkwhale.ffa.utils.OAuth
import audio.funkwhale.ffa.utils.getMetadata
import audio.funkwhale.ffa.utils.mustNormalizeUrl
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.google.android.exoplayer2.offline.Download
import com.google.android.exoplayer2.offline.DownloadManager
@ -23,7 +29,7 @@ class TracksRepository(override val context: Context?, albumId: Int) :
override val cacheId = "tracks-album-$albumId"
override val upstream = HttpUpstream<Track, OtterResponse<Track>>(
override val upstream = HttpUpstream<Track, FFAResponse<Track>>(
context,
HttpUpstream.Behavior.AtOnce,
"/api/v1/tracks/?playable=true&album=$albumId&ordering=disc_number,position",

View File

@ -1,6 +1,8 @@
package audio.funkwhale.ffa.utils
import audio.funkwhale.ffa.FFA
import audio.funkwhale.ffa.model.Radio
import audio.funkwhale.ffa.model.Track
import com.google.android.exoplayer2.offline.Download
import com.google.android.exoplayer2.offline.DownloadCursor
import kotlinx.coroutines.Dispatchers.IO

View File

@ -2,11 +2,11 @@ package audio.funkwhale.ffa.utils
import android.content.Context
import android.os.Build
import android.os.SystemClock
import android.util.Log
import androidx.fragment.app.Fragment
import audio.funkwhale.ffa.R
import audio.funkwhale.ffa.fragments.BrowseFragment
import audio.funkwhale.ffa.model.DownloadInfo
import audio.funkwhale.ffa.repositories.Repository
import com.github.kittinunf.fuel.core.FuelError
import com.github.kittinunf.fuel.core.Request
@ -26,7 +26,6 @@ import java.text.SimpleDateFormat
import java.util.*
import kotlin.coroutines.CoroutineContext
inline fun <D> Flow<Repository.Response<D>>.untilNetwork(
scope: CoroutineScope,
context: CoroutineContext = Main,

View File

@ -7,6 +7,7 @@ import java.nio.charset.Charset
import java.security.MessageDigest
object FFACache {
private fun key(key: String): String {
val md = MessageDigest.getInstance("SHA-1")
val digest = md.digest(key.toByteArray(Charset.defaultCharset()))
@ -35,4 +36,4 @@ object FFACache {
delete()
}
}
}
}

View File

@ -1,213 +0,0 @@
package audio.funkwhale.ffa.utils
import com.google.android.exoplayer2.offline.Download
import com.preference.PowerPreference
data class User(
val full_username: String
)
sealed class CacheItem<D : Any>(val data: List<D>)
class ArtistsCache(data: List<Artist>) : CacheItem<Artist>(data)
class AlbumsCache(data: List<Album>) : CacheItem<Album>(data)
class TracksCache(data: List<Track>) : CacheItem<Track>(data)
class PlaylistsCache(data: List<Playlist>) : CacheItem<Playlist>(data)
class PlaylistTracksCache(data: List<PlaylistTrack>) : CacheItem<PlaylistTrack>(data)
class RadiosCache(data: List<Radio>) : CacheItem<Radio>(data)
class FavoritedCache(data: List<Int>) : CacheItem<Int>(data)
class QueueCache(data: List<Track>) : CacheItem<Track>(data)
abstract class OtterResponse<D : Any> {
abstract val count: Int
abstract val next: String?
abstract fun getData(): List<D>
}
data class UserResponse(
override val count: Int,
override val next: String?,
val results: List<Artist>
) : OtterResponse<Artist>() {
override fun getData() = results
}
data class ArtistsResponse(
override val count: Int,
override val next: String?,
val results: List<Artist>
) : OtterResponse<Artist>() {
override fun getData() = results
}
data class AlbumsResponse(
override val count: Int,
override val next: String?,
val results: AlbumList
) : OtterResponse<Album>() {
override fun getData() = results
}
data class TracksResponse(
override val count: Int,
override val next: String?,
val results: List<Track>
) : OtterResponse<Track>() {
override fun getData() = results
}
data class FavoritedResponse(
override val count: Int,
override val next: String?,
val results: List<Favorited>
) : OtterResponse<Int>() {
override fun getData() = results.map { it.track }
}
data class PlaylistsResponse(
override val count: Int,
override val next: String?,
val results: List<Playlist>
) : OtterResponse<Playlist>() {
override fun getData() = results
}
data class PlaylistTracksResponse(
override val count: Int,
override val next: String?,
val results: List<PlaylistTrack>
) : OtterResponse<PlaylistTrack>() {
override fun getData() = results
}
data class RadiosResponse(
override val count: Int,
override val next: String?,
val results: List<Radio>
) : OtterResponse<Radio>() {
override fun getData() = results
}
data class Covers(val urls: CoverUrls)
data class CoverUrls(val original: String)
typealias AlbumList = List<Album>
interface SearchResult {
fun cover(): String?
fun title(): String
fun subtitle(): String
}
data class Album(
val id: Int,
val artist: Artist,
val title: String,
val cover: Covers?,
val release_date: String?
) : SearchResult {
data class Artist(val name: String)
override fun cover() = cover?.urls?.original
override fun title() = title
override fun subtitle() = artist.name
}
data class Artist(
val id: Int,
val name: String,
val albums: List<Album>?
) : SearchResult {
data class Album(
val title: String,
val cover: Covers?
)
override fun cover(): String? = albums?.getOrNull(0)?.cover?.urls?.original
override fun title() = name
override fun subtitle() = "Artist"
}
data class Track(
val id: Int = 0,
val title: String,
val artist: Artist,
val album: Album?,
val disc_number: Int = 0,
val position: Int = 0,
val uploads: List<Upload> = listOf(),
val copyright: String? = null,
val license: String? = null
) : SearchResult {
var current: Boolean = false
var favorite: Boolean = false
var cached: Boolean = false
var downloaded: Boolean = false
companion object {
fun fromDownload(download: DownloadInfo): Track = Track(
id = download.id,
title = download.title,
artist = Artist(0, download.artist, listOf()),
album = Album(0, Album.Artist(""), "", Covers(CoverUrls("")), ""),
uploads = listOf(Upload(download.contentId, 0, 0))
)
}
data class Upload(
val listen_url: String,
val duration: Int,
val bitrate: Int
)
override fun equals(other: Any?): Boolean {
return when (other) {
is Track -> other.id == id
else -> false
}
}
fun bestUpload(): Upload? {
if (uploads.isEmpty()) return null
return when (PowerPreference.getDefaultFile().getString("media_cache_quality")) {
"quality" -> uploads.maxByOrNull { it.bitrate } ?: uploads[0]
"size" -> uploads.minByOrNull { it.bitrate } ?: uploads[0]
else -> uploads.maxByOrNull { it.bitrate } ?: uploads[0]
}
}
override fun cover() = album?.cover?.urls?.original
override fun title() = title
override fun subtitle() = artist.name
val formatted: String get() = "$id $artist ($album): $title"
}
data class Favorited(val track: Int)
data class Playlist(
val id: Int,
val name: String,
val album_covers: List<String>,
val tracks_count: Int,
val duration: Int
)
data class PlaylistTrack(val track: Track)
data class Radio(
val id: Int,
var radio_type: String,
val name: String,
val description: String,
var related_object_id: String? = null
)
data class DownloadInfo(
val id: Int,
val contentId: String,
val title: String,
val artist: String,
var download: Download?
)

View File

@ -1,4 +1,3 @@
package audio.funkwhale.ffa.utils
object RefreshError : Throwable()

View File

@ -1,6 +1,7 @@
package audio.funkwhale.ffa.utils
import android.content.Context
import audio.funkwhale.ffa.model.User
import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
import com.github.kittinunf.fuel.gson.gsonDeserializerOf

View File

@ -7,7 +7,7 @@ buildscript {
}
dependencies {
classpath("com.android.tools.build:gradle:${Versions.androidGradlePlugin}")
classpath("com.android.tools.build:gradle:7.0.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}")
classpath("com.github.bjoernq:unmockplugin:${Versions.unmock}")
classpath("com.github.ben-manes:gradle-versions-plugin:${Versions.gradleDependencyPlugin}")

View File

@ -2,7 +2,6 @@ object Versions {
const val kotlin = "1.5.21"
const val jacoco = "0.8.7"
const val unmock = "0.7.8"
const val androidGradlePlugin = "7.0.0"
const val gradleDependencyPlugin = "0.38.0"
const val exoPlayer = "2.11.8"