mirror of
https://github.com/apognu/otter
synced 2025-03-12 13:30:04 +01:00
Several improvements in UI (better colors for night mode, added icons).
Better handling of startup (login activity would reset if put in the background). Allow use of schemeless hostname for login. Destroy main activity and clear cache on logout. Change of endpoint for favorites retrieval for one with much better performance.
This commit is contained in:
parent
78468167ca
commit
e84455390b
@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.github.apognu.otter">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
|
||||
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
|
||||
|
||||
<application
|
||||
android:name="com.github.apognu.otter.Otter"
|
||||
@ -14,31 +13,39 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<!-- <meta-data
|
||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||
android:value="com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider"/> -->
|
||||
|
||||
<activity android:name="com.github.apognu.otter.activities.LoginActivity" android:noHistory="true" android:launchMode="singleInstance">
|
||||
<activity
|
||||
android:name="com.github.apognu.otter.activities.SplashActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:noHistory="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.github.apognu.otter.activities.MainActivity"/>
|
||||
<activity android:name="com.github.apognu.otter.activities.SearchActivity" android:launchMode="singleTop"/>
|
||||
<activity android:name="com.github.apognu.otter.activities.SettingsActivity"/>
|
||||
<activity android:name="com.github.apognu.otter.activities.LicencesActivity"/>
|
||||
<activity
|
||||
android:name="com.github.apognu.otter.activities.LoginActivity"
|
||||
android:launchMode="singleInstance" />
|
||||
<activity android:name="com.github.apognu.otter.activities.MainActivity" />
|
||||
<activity
|
||||
android:name="com.github.apognu.otter.activities.SearchActivity"
|
||||
android:launchMode="singleTop" />
|
||||
<activity android:name="com.github.apognu.otter.activities.SettingsActivity" />
|
||||
<activity android:name="com.github.apognu.otter.activities.LicencesActivity" />
|
||||
|
||||
<service android:name="com.github.apognu.otter.playback.PlayerService"/>
|
||||
<service android:name="com.github.apognu.otter.playback.PlayerService" />
|
||||
|
||||
<receiver android:name="com.github.apognu.otter.playback.MediaControlActionReceiver"/>
|
||||
<receiver android:name="com.github.apognu.otter.playback.MediaControlActionReceiver" />
|
||||
|
||||
</application>
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.github.apognu.otter.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.github.apognu.otter.R
|
||||
import com.github.apognu.otter.fragments.LoginDialog
|
||||
@ -19,33 +19,29 @@ import kotlinx.coroutines.launch
|
||||
data class FwCredentials(val token: String)
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_login)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
getSharedPreferences(AppContext.PREFS_CREDENTIALS, Context.MODE_PRIVATE).apply {
|
||||
when (contains("access_token")) {
|
||||
true -> Intent(this@LoginActivity, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NO_ANIMATION
|
||||
|
||||
startActivity(this)
|
||||
}
|
||||
|
||||
false -> setContentView(R.layout.activity_login)
|
||||
}
|
||||
}
|
||||
|
||||
login?.setOnClickListener {
|
||||
val hostname = hostname.text.toString().trim()
|
||||
var hostname = hostname.text.toString().trim()
|
||||
val username = username.text.toString()
|
||||
val password = password.text.toString()
|
||||
|
||||
try {
|
||||
if (hostname.isEmpty()) throw Exception(getString(R.string.login_error_hostname))
|
||||
|
||||
val url = Uri.parse(hostname)
|
||||
Uri.parse(hostname).apply {
|
||||
if (scheme == "http") {
|
||||
throw Exception(getString(R.string.login_error_hostname_https))
|
||||
}
|
||||
|
||||
if (url.scheme != "https") {
|
||||
throw Exception(getString(R.string.login_error_hostname_https))
|
||||
if (scheme == null) hostname = "https://${hostname}"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val message =
|
||||
|
@ -19,6 +19,7 @@ import com.github.apognu.otter.fragments.BrowseFragment
|
||||
import com.github.apognu.otter.fragments.QueueFragment
|
||||
import com.github.apognu.otter.playback.MediaControlsManager
|
||||
import com.github.apognu.otter.playback.PlayerService
|
||||
import com.github.apognu.otter.repositories.FavoritedRepository
|
||||
import com.github.apognu.otter.repositories.FavoritesRepository
|
||||
import com.github.apognu.otter.repositories.Repository
|
||||
import com.github.apognu.otter.utils.*
|
||||
@ -32,7 +33,12 @@ import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
enum class ResultCode(val code: Int) {
|
||||
LOGOUT(1001)
|
||||
}
|
||||
|
||||
private val favoriteRepository = FavoritesRepository(this)
|
||||
private val favoriteCheckRepository = FavoritedRepository(this)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -127,12 +133,25 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
R.id.nav_queue -> launchDialog(QueueFragment())
|
||||
R.id.nav_search -> startActivity(Intent(this, SearchActivity::class.java))
|
||||
R.id.settings -> startActivity(Intent(this, SettingsActivity::class.java))
|
||||
R.id.settings -> startActivityForResult(Intent(this, SettingsActivity::class.java), 0)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
if (resultCode == ResultCode.LOGOUT.code) {
|
||||
Intent(this, LoginActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
|
||||
startActivity(this)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchFragment(fragment: Fragment) {
|
||||
supportFragmentManager.fragments.lastOrNull()?.also { oldFragment ->
|
||||
oldFragment.enterTransition = null
|
||||
@ -235,11 +254,10 @@ class MainActivity : AppCompatActivity() {
|
||||
.centerCrop()
|
||||
.into(now_playing_details_cover)
|
||||
|
||||
favoriteRepository.fetch().untilNetwork(IO) { favorites ->
|
||||
favoriteCheckRepository.fetch().untilNetwork(IO) { favorites ->
|
||||
GlobalScope.launch(Main) {
|
||||
val favorites = favorites.map { it.track.id }
|
||||
|
||||
track.favorite = favorites.contains(track.id)
|
||||
|
||||
when (track.favorite) {
|
||||
true -> now_playing_details_favorite.setColorFilter(getColor(R.color.colorFavorite))
|
||||
false -> now_playing_details_favorite.setColorFilter(getColor(R.color.controlForeground))
|
||||
|
@ -56,12 +56,10 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
.setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).clear()
|
||||
|
||||
Intent(context, LoginActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
context.cacheDir.deleteRecursively()
|
||||
|
||||
startActivity(this)
|
||||
activity?.finish()
|
||||
}
|
||||
activity?.setResult(MainActivity.ResultCode.LOGOUT.code)
|
||||
activity?.finish()
|
||||
}
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show()
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.github.apognu.otter.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.github.apognu.otter.utils.AppContext
|
||||
|
||||
class SplashActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
getSharedPreferences(AppContext.PREFS_CREDENTIALS, Context.MODE_PRIVATE).apply {
|
||||
when (contains("access_token")) {
|
||||
true -> Intent(this@SplashActivity, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NO_ANIMATION
|
||||
|
||||
startActivity(this)
|
||||
}
|
||||
|
||||
false -> Intent(this@SplashActivity, LoginActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NO_ANIMATION
|
||||
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
|
||||
import kotlinx.android.synthetic.main.row_track.view.*
|
||||
import java.util.*
|
||||
|
||||
class FavoritesAdapter(private val context: Context?, private val favoriteListener: OnFavoriteListener, val fromQueue: Boolean = false) : FunkwhaleAdapter<Favorite, FavoritesAdapter.ViewHolder>() {
|
||||
class FavoritesAdapter(private val context: Context?, private val favoriteListener: OnFavoriteListener, val fromQueue: Boolean = false) : FunkwhaleAdapter<Track, FavoritesAdapter.ViewHolder>() {
|
||||
interface OnFavoriteListener {
|
||||
fun onToggleFavorite(id: Int, state: Boolean)
|
||||
}
|
||||
@ -28,7 +28,7 @@ class FavoritesAdapter(private val context: Context?, private val favoriteListen
|
||||
override fun getItemCount() = data.size
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return data[position].track.id.toLong()
|
||||
return data[position].id.toLong()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
@ -44,14 +44,14 @@ class FavoritesAdapter(private val context: Context?, private val favoriteListen
|
||||
val favorite = data[position]
|
||||
|
||||
Picasso.get()
|
||||
.maybeLoad(maybeNormalizeUrl(favorite.track.album.cover.original))
|
||||
.maybeLoad(maybeNormalizeUrl(favorite.album.cover.original))
|
||||
.fit()
|
||||
.placeholder(R.drawable.cover)
|
||||
.transform(RoundedCornersTransformation(16, 0))
|
||||
.into(holder.cover)
|
||||
|
||||
holder.title.text = favorite.track.title
|
||||
holder.artist.text = favorite.track.artist.name
|
||||
holder.title.text = favorite.title
|
||||
holder.artist.text = favorite.artist.name
|
||||
|
||||
Build.VERSION_CODES.P.onApi(
|
||||
{
|
||||
@ -64,19 +64,19 @@ class FavoritesAdapter(private val context: Context?, private val favoriteListen
|
||||
})
|
||||
|
||||
|
||||
if (favorite.track == currentTrack || favorite.track.current) {
|
||||
if (favorite == currentTrack || favorite.current) {
|
||||
holder.title.setTypeface(holder.title.typeface, Typeface.BOLD)
|
||||
holder.artist.setTypeface(holder.artist.typeface, Typeface.BOLD)
|
||||
}
|
||||
|
||||
context?.let {
|
||||
when (favorite.track.favorite) {
|
||||
when (favorite.favorite) {
|
||||
true -> holder.favorite.setColorFilter(context.getColor(R.color.colorFavorite))
|
||||
false -> holder.favorite.setColorFilter(context.getColor(R.color.colorSelected))
|
||||
}
|
||||
|
||||
holder.favorite.setOnClickListener {
|
||||
favoriteListener.onToggleFavorite(favorite.track.id, !favorite.track.favorite)
|
||||
favoriteListener.onToggleFavorite(favorite.id, !favorite.favorite)
|
||||
|
||||
data.remove(favorite)
|
||||
notifyItemRemoved(holder.adapterPosition)
|
||||
@ -90,9 +90,9 @@ class FavoritesAdapter(private val context: Context?, private val favoriteListen
|
||||
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.track_add_to_queue -> CommandBus.send(Command.AddToQueue(listOf(favorite.track)))
|
||||
R.id.track_play_next -> CommandBus.send(Command.PlayNext(favorite.track))
|
||||
R.id.queue_remove -> CommandBus.send(Command.RemoveFromQueue(favorite.track))
|
||||
R.id.track_add_to_queue -> CommandBus.send(Command.AddToQueue(listOf(favorite)))
|
||||
R.id.track_play_next -> CommandBus.send(Command.PlayNext(favorite))
|
||||
R.id.queue_remove -> CommandBus.send(Command.RemoveFromQueue(favorite))
|
||||
}
|
||||
|
||||
true
|
||||
@ -132,7 +132,7 @@ class FavoritesAdapter(private val context: Context?, private val favoriteListen
|
||||
true -> CommandBus.send(Command.PlayTrack(layoutPosition))
|
||||
false -> {
|
||||
data.subList(layoutPosition, data.size).plus(data.subList(0, layoutPosition)).apply {
|
||||
CommandBus.send(Command.ReplaceQueue(this.map { it.track }))
|
||||
CommandBus.send(Command.ReplaceQueue(this))
|
||||
|
||||
context.toast("All tracks were added to your queue")
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FavoritesFragment : FunkwhaleFragment<Favorite, FavoritesAdapter>() {
|
||||
class FavoritesFragment : FunkwhaleFragment<Track, FavoritesAdapter>() {
|
||||
override val viewRes = R.layout.fragment_favorites
|
||||
override val recycler: RecyclerView get() = favorites
|
||||
|
||||
@ -22,7 +22,6 @@ class FavoritesFragment : FunkwhaleFragment<Favorite, FavoritesAdapter>() {
|
||||
|
||||
adapter = FavoritesAdapter(context, FavoriteListener())
|
||||
repository = FavoritesRepository(context)
|
||||
favoritesRepository = FavoritesRepository(context)
|
||||
|
||||
watchEventBus()
|
||||
}
|
||||
@ -38,7 +37,7 @@ class FavoritesFragment : FunkwhaleFragment<Favorite, FavoritesAdapter>() {
|
||||
}
|
||||
|
||||
play.setOnClickListener {
|
||||
CommandBus.send(Command.ReplaceQueue(adapter.data.shuffled().map { it.track }))
|
||||
CommandBus.send(Command.ReplaceQueue(adapter.data.shuffled()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,16 +12,16 @@ import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.BufferedReader
|
||||
|
||||
class FavoritesRepository(override val context: Context?) : Repository<Favorite, FavoritesCache>() {
|
||||
override val cacheId = "favorites"
|
||||
override val upstream = HttpUpstream<Favorite, FunkwhaleResponse<Favorite>>(HttpUpstream.Behavior.AtOnce, "/api/v1/favorites/tracks?playable=true", object : TypeToken<FavoritesResponse>() {}.type)
|
||||
class FavoritesRepository(override val context: Context?) : Repository<Track, TracksCache>() {
|
||||
override val cacheId = "favorites.v2"
|
||||
override val upstream = HttpUpstream<Track, FunkwhaleResponse<Track>>(HttpUpstream.Behavior.AtOnce, "/api/v1/tracks?favorites=true&playable=true", object : TypeToken<TracksResponse>() {}.type)
|
||||
|
||||
override fun cache(data: List<Favorite>) = FavoritesCache(data)
|
||||
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(FavoritesCache::class.java).deserialize(reader)
|
||||
override fun cache(data: List<Track>) = TracksCache(data)
|
||||
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(TracksCache::class.java).deserialize(reader)
|
||||
|
||||
override fun onDataFetched(data: List<Favorite>) = data.map {
|
||||
override fun onDataFetched(data: List<Track>) = data.map {
|
||||
it.apply {
|
||||
it.track.favorite = true
|
||||
it.favorite = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,3 +53,11 @@ class FavoritesRepository(override val context: Context?) : Repository<Favorite,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FavoritedRepository(override val context: Context?) : Repository<Int, FavoritedCache>() {
|
||||
override val cacheId = "favorited"
|
||||
override val upstream = HttpUpstream<Int, FunkwhaleResponse<Int>>(HttpUpstream.Behavior.Single, "/api/v1/favorites/tracks/all?playable=true", object : TypeToken<FavoritedResponse>() {}.type)
|
||||
|
||||
override fun cache(data: List<Int>) = FavoritedCache(data)
|
||||
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(FavoritedCache::class.java).deserialize(reader)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class HttpUpstream<D : Any, R : FunkwhaleResponse<D>>(private val behavior: Beha
|
||||
}
|
||||
|
||||
override fun fetch(data: List<D>): Channel<Repository.Response<D>>? {
|
||||
if (behavior == Behavior.Single && data.isNotEmpty()) return null
|
||||
if (behavior == Behavior.Single && data.isNotEmpty()) return null
|
||||
|
||||
val page = ceil(data.size / AppContext.PAGE_SIZE.toDouble()).toInt() + 1
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
package com.github.apognu.otter.repositories
|
||||
|
||||
import android.content.Context
|
||||
import com.github.apognu.otter.utils.*
|
||||
import com.github.apognu.otter.utils.FunkwhaleResponse
|
||||
import com.github.apognu.otter.utils.PlaylistTrack
|
||||
import com.github.apognu.otter.utils.PlaylistTracksCache
|
||||
import com.github.apognu.otter.utils.PlaylistTracksResponse
|
||||
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@ -15,17 +18,10 @@ class PlaylistTracksRepository(override val context: Context?, playlistId: Int)
|
||||
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(PlaylistTracksCache::class.java).deserialize(reader)
|
||||
|
||||
override fun onDataFetched(data: List<PlaylistTrack>): List<PlaylistTrack> = runBlocking {
|
||||
val favorites = FavoritesRepository(context).fetch(Origin.Network.origin).receive().data
|
||||
|
||||
log(favorites.toString())
|
||||
val favorites = FavoritedRepository(context).fetch(Origin.Network.origin).receive().data
|
||||
|
||||
data.map { track ->
|
||||
val favorite = favorites.find { it.track.id == track.track.id }
|
||||
|
||||
if (favorite != null) {
|
||||
track.track.favorite = true
|
||||
}
|
||||
|
||||
track.track.favorite = favorites.contains(track.track.id)
|
||||
track
|
||||
}
|
||||
}
|
||||
|
@ -18,15 +18,10 @@ class SearchRepository(override val context: Context?, query: String) : Reposito
|
||||
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(TracksCache::class.java).deserialize(reader)
|
||||
|
||||
override fun onDataFetched(data: List<Track>): List<Track> = runBlocking {
|
||||
val favorites = FavoritesRepository(context).fetch(Origin.Network.origin).receive().data
|
||||
val favorites = FavoritedRepository(context).fetch(Origin.Network.origin).receive().data
|
||||
|
||||
data.map { track ->
|
||||
val favorite = favorites.find { it.track.id == track.id }
|
||||
|
||||
if (favorite != null) {
|
||||
track.favorite = true
|
||||
}
|
||||
|
||||
track.favorite = favorites.contains(track.id)
|
||||
track
|
||||
}
|
||||
}
|
||||
|
@ -18,15 +18,10 @@ class TracksRepository(override val context: Context?, albumId: Int) : Repositor
|
||||
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(TracksCache::class.java).deserialize(reader)
|
||||
|
||||
override fun onDataFetched(data: List<Track>): List<Track> = runBlocking {
|
||||
val favorites = FavoritesRepository(context).fetch(Origin.Network.origin).receive().data
|
||||
val favorites = FavoritedRepository(context).fetch(Origin.Network.origin).receive().data
|
||||
|
||||
data.map { track ->
|
||||
val favorite = favorites.find { it.track.id == track.id }
|
||||
|
||||
if (favorite != null) {
|
||||
track.favorite = true
|
||||
}
|
||||
|
||||
track.favorite = favorites.contains(track.id)
|
||||
track
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ 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 FavoritesCache(data: List<Favorite>) : CacheItem<Favorite>(data)
|
||||
class FavoritedCache(data: List<Int>) : CacheItem<Int>(data)
|
||||
class QueueCache(data: List<Track>) : CacheItem<Track>(data)
|
||||
|
||||
abstract class FunkwhaleResponse<D : Any> {
|
||||
@ -18,6 +18,10 @@ abstract class FunkwhaleResponse<D : Any> {
|
||||
abstract fun getData(): List<D>
|
||||
}
|
||||
|
||||
data class UserResponse(override val count: Int, override val next: String?, val results: List<Artist>) : FunkwhaleResponse<Artist>() {
|
||||
override fun getData() = results
|
||||
}
|
||||
|
||||
data class ArtistsResponse(override val count: Int, override val next: String?, val results: List<Artist>) : FunkwhaleResponse<Artist>() {
|
||||
override fun getData() = results
|
||||
}
|
||||
@ -30,8 +34,8 @@ data class TracksResponse(override val count: Int, override val next: String?, v
|
||||
override fun getData() = results
|
||||
}
|
||||
|
||||
data class FavoritesResponse(override val count: Int, override val next: String?, val results: List<Favorite>) : FunkwhaleResponse<Favorite>() {
|
||||
override fun getData() = results
|
||||
data class FavoritedResponse(override val count: Int, override val next: String?, val results: List<Favorited>) : FunkwhaleResponse<Int>() {
|
||||
override fun getData() = results.map { it.track }
|
||||
}
|
||||
|
||||
data class PlaylistsResponse(override val count: Int, override val next: String?, val results: List<Playlist>) : FunkwhaleResponse<Playlist>() {
|
||||
@ -100,7 +104,7 @@ data class Track(
|
||||
}
|
||||
}
|
||||
|
||||
data class Favorite(val id: Int, val track: Track)
|
||||
data class Favorited(val track: Int)
|
||||
|
||||
data class Playlist(
|
||||
val id: Int,
|
||||
|
9
app/src/main/res/drawable/brightness.xml
Normal file
9
app/src/main/res/drawable/brightness.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-0.89,0 -1.74,-0.2 -2.5,-0.55C11.56,16.5 13,14.42 13,12s-1.44,-4.5 -3.5,-5.45C10.26,6.2 11.11,6 12,6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/logout.xml
Normal file
9
app/src/main/res/drawable/logout.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/quality.xml
Normal file
9
app/src/main/res/drawable/quality.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM11,15L9.5,15v-2h-2v2L6,15L6,9h1.5v2.5h2L9.5,9L11,9v6zM18,14c0,0.55 -0.45,1 -1,1h-0.75v1.5h-1.5L14.75,15L14,15c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v4zM14.5,13.5h2v-3h-2v3z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/storage.xml
Normal file
9
app/src/main/res/drawable/storage.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM12,8h-2L10,4h2v4zM15,8h-2L13,4h2v4zM18,8h-2L16,4h2v4z"/>
|
||||
</vector>
|
@ -33,6 +33,7 @@
|
||||
android:hint="@string/login_hostname"
|
||||
android:textColorHint="@drawable/login_input"
|
||||
app:boxStrokeColor="@drawable/login_input"
|
||||
app:errorTextAppearance="@style/AppTheme.ErrorStyle"
|
||||
app:hintTextColor="@drawable/login_input">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
@ -54,6 +55,7 @@
|
||||
android:hint="@string/login_username"
|
||||
android:textColorHint="@drawable/login_input"
|
||||
app:boxStrokeColor="@drawable/login_input"
|
||||
app:errorTextAppearance="@style/AppTheme.ErrorStyle"
|
||||
app:hintTextColor="@drawable/login_input">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
@ -75,6 +77,7 @@
|
||||
android:hint="@string/login_password"
|
||||
android:textColorHint="@drawable/login_input"
|
||||
app:boxStrokeColor="@drawable/login_input"
|
||||
app:errorTextAppearance="@style/AppTheme.ErrorStyle"
|
||||
app:hintTextColor="@drawable/login_input"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
@ -94,5 +97,5 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/colorAccent"
|
||||
android:text="@string/login_submit"
|
||||
android:textColor="@android:color/white" />
|
||||
android:textColor="@color/whiteWhileLight" />
|
||||
</LinearLayout>
|
||||
|
@ -3,12 +3,16 @@
|
||||
<color name="surface">#121212</color>
|
||||
|
||||
<color name="colorPrimary">#283f4e</color>
|
||||
<color name="colorAccent">#99440c</color>
|
||||
<color name="colorAccent">#f1b44f</color>
|
||||
|
||||
<color name="colorSelected">#525252</color>
|
||||
<color name="colorFavorite">#eba999</color>
|
||||
|
||||
<color name="itemTitle">#caffffff</color>
|
||||
|
||||
<color name="controlForeground">#caffffff</color>
|
||||
<color name="controlColor">#53bce7</color>
|
||||
|
||||
<color name="controlColor">#327eae</color>
|
||||
<color name="whiteWhileLight">#000000</color>
|
||||
<color name="blackWhileLight">#ffffff</color>
|
||||
</resources>
|
@ -6,6 +6,7 @@
|
||||
<color name="colorPrimary">#327eae</color>
|
||||
<color name="colorPrimaryDark">#3d3e40</color>
|
||||
<color name="colorAccent">#d35400</color>
|
||||
<color name="colorError">#b94705</color>
|
||||
|
||||
<color name="colorSelected">#dadada</color>
|
||||
<color name="colorFavorite">#e17055</color>
|
||||
@ -14,4 +15,7 @@
|
||||
|
||||
<color name="controlForeground">@color/colorPrimary</color>
|
||||
<color name="controlColor">@color/colorPrimary</color>
|
||||
|
||||
<color name="whiteWhileLight">#ffffff</color>
|
||||
<color name="blackWhileLight">#000000</color>
|
||||
</resources>
|
||||
|
@ -27,8 +27,9 @@
|
||||
|
||||
<style name="AppTheme.Title">
|
||||
<item name="android:fontFamily">sans-serif-light</item>
|
||||
<item name="android:textSize">28sp</item>
|
||||
<item name="android:textSize">24sp</item>
|
||||
<item name="android:textColor">@color/itemTitle</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.ItemTitle">
|
||||
@ -44,6 +45,7 @@
|
||||
|
||||
<style name="AppTheme.Preference" parent="PreferenceThemeOverlay">
|
||||
<item name="android:textColor">@color/itemTitle</item>
|
||||
<item name="android:tint">@color/blackWhileLight</item>
|
||||
|
||||
<item name="preferenceCategoryStyle">@style/AppTheme.PreferenceCategory</item>
|
||||
</style>
|
||||
@ -74,4 +76,8 @@
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.ErrorStyle" parent="@android:style/TextAppearance">
|
||||
<item name="android:textColor">@color/colorError</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -8,11 +8,13 @@
|
||||
android:defaultValue="quality"
|
||||
android:entries="@array/media_qualities"
|
||||
android:entryValues="@array/media_qualities_values"
|
||||
android:icon="@drawable/quality"
|
||||
android:key="media_quality"
|
||||
android:title="@string/settings_media_quality" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="1"
|
||||
android:icon="@drawable/storage"
|
||||
android:key="media_cache_size"
|
||||
android:max="5"
|
||||
android:min="0"
|
||||
@ -28,14 +30,17 @@
|
||||
android:defaultValue="system"
|
||||
android:entries="@array/night_mode"
|
||||
android:entryValues="@array/night_mode_values"
|
||||
android:icon="@drawable/brightness"
|
||||
android:key="night_mode"
|
||||
android:title="@string/settings_night_mode" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/favorite"
|
||||
android:key="oss_licences"
|
||||
android:title="@string/title_oss_licences" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/logout"
|
||||
android:key="logout"
|
||||
android:title="@string/settings_logout" />
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user