diff --git a/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt b/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt index 7d9c5f6..2e81e50 100644 --- a/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt +++ b/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt @@ -8,6 +8,7 @@ import androidx.appcompat.app.AppCompatActivity import com.github.apognu.otter.R import com.github.apognu.otter.fragments.LoginDialog import com.github.apognu.otter.utils.AppContext +import com.github.apognu.otter.utils.Userinfo import com.github.kittinunf.fuel.Fuel import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult import com.github.kittinunf.fuel.gson.gsonDeserializerOf @@ -103,9 +104,13 @@ class LoginActivity : AppCompatActivity() { setString("access_token", result.get().token) } - dialog.dismiss() - startActivity(Intent(this@LoginActivity, MainActivity::class.java)) - finish() + Userinfo.get()?.let { + dialog.dismiss() + startActivity(Intent(this@LoginActivity, MainActivity::class.java)) + finish() + } + + throw Exception(getString(R.string.login_error_userinfo)) } is Result.Failure -> { diff --git a/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt b/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt index cbab4fd..280475b 100644 --- a/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt +++ b/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt @@ -88,6 +88,10 @@ class MainActivity : AppCompatActivity() { startService(Intent(this, PlayerService::class.java)) DownloadService.start(this, PinService::class.java) + GlobalScope.launch(IO) { + Userinfo.get() + } + now_playing_toggle.setOnClickListener { CommandBus.send(Command.ToggleState) } diff --git a/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt b/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt index 9ece861..07661a9 100644 --- a/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt +++ b/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt @@ -7,11 +7,11 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.fragments.FunkwhaleAdapter -import com.github.apognu.otter.utils.Event -import com.github.apognu.otter.utils.EventBus -import com.github.apognu.otter.utils.Radio +import com.github.apognu.otter.utils.* import com.github.apognu.otter.views.LoadingImageView +import com.preference.PowerPreference import kotlinx.android.synthetic.main.row_radio.view.* +import kotlinx.android.synthetic.main.row_radio_header.view.* import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect @@ -22,43 +22,105 @@ class RadiosAdapter(val context: Context?, private val listener: OnRadioClickLis fun onClick(holder: ViewHolder, radio: Radio) } - override fun getItemCount() = data.size + enum class RowType { + Header, + InstanceRadio, + UserRadio + } + + private val instanceRadios: List by lazy { + context?.let { + return@lazy when (val username = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("actor_username")) { + "" -> listOf( + Radio(0, "random", context.getString(R.string.radio_random_title), context.getString(R.string.radio_random_description)) + ) + + else -> listOf( + Radio(0, "actor_content", context.getString(R.string.radio_your_content_title), context.getString(R.string.radio_your_content_description), username), + Radio(0, "random", context.getString(R.string.radio_random_title), context.getString(R.string.radio_random_description)), + Radio(0, "favorites", context.getString(R.string.favorites), context.getString(R.string.radio_favorites_description)), + Radio(0, "less-listened", context.getString(R.string.radio_less_listened_title), context.getString(R.string.radio_less_listened_description)) + ) + } + } + + listOf() + } + + private fun getRadioAt(position: Int): Radio { + return when (getItemViewType(position)) { + RowType.InstanceRadio.ordinal -> instanceRadios[position - 1] + else -> data[position - instanceRadios.size - 2] + } + } + + override fun getItemCount() = instanceRadios.size + data.size + 2 override fun getItemId(position: Int) = data[position].id.toLong() + override fun getItemViewType(position: Int): Int { + return when { + position == 0 || position == instanceRadios.size + 1 -> RowType.Header.ordinal + position <= instanceRadios.size -> RowType.InstanceRadio.ordinal + else -> RowType.UserRadio.ordinal + } + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RadiosAdapter.ViewHolder { - val view = LayoutInflater.from(context).inflate(R.layout.row_radio, parent, false) + return when (viewType) { + RowType.InstanceRadio.ordinal, RowType.UserRadio.ordinal -> { + val view = LayoutInflater.from(context).inflate(R.layout.row_radio, parent, false) + + ViewHolder(view, listener).also { + view.setOnClickListener(it) + } + } - return ViewHolder(view, listener).also { - view.setOnClickListener(it) + else -> ViewHolder(LayoutInflater.from(context).inflate(R.layout.row_radio_header, parent, false), null) } } override fun onBindViewHolder(holder: RadiosAdapter.ViewHolder, position: Int) { - val radio = data[position] + when (getItemViewType(position)) { + RowType.Header.ordinal -> { + context?.let { + when (position) { + 0 -> holder.label.text = context.getString(R.string.radio_instance_radios) + instanceRadios.size + 1 -> holder.label.text = context.getString(R.string.radio_user_radios) + } + } + } - holder.art.visibility = View.VISIBLE - holder.name.text = radio.name - holder.description.text = radio.description + RowType.InstanceRadio.ordinal, RowType.UserRadio.ordinal -> { + val radio = getRadioAt(position) - context?.let { context -> - val icon = when (radio.radio_type) { - "random" -> R.drawable.shuffle - "less-listened" -> R.drawable.sad - else -> null - } + holder.art.visibility = View.VISIBLE + holder.name.text = radio.name + holder.description.text = radio.description + + context?.let { context -> + val icon = when (radio.radio_type) { + "actor_content" -> R.drawable.library + "favorites" -> R.drawable.favorite + "random" -> R.drawable.shuffle + "less-listened" -> R.drawable.sad + else -> null + } - icon?.let { - holder.native = true + icon?.let { + holder.native = true - holder.art.setImageDrawable(context.getDrawable(icon)) - holder.art.alpha = 0.7f - holder.art.setColorFilter(context.getColor(R.color.controlForeground)) + holder.art.setImageDrawable(context.getDrawable(icon)) + holder.art.alpha = 0.7f + holder.art.setColorFilter(context.getColor(R.color.controlForeground)) + } + } } } } - inner class ViewHolder(view: View, private val listener: OnRadioClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener { + inner class ViewHolder(view: View, private val listener: OnRadioClickListener?) : RecyclerView.ViewHolder(view), View.OnClickListener { + val label = view.label val art = view.art val name = view.name val description = view.description @@ -66,7 +128,7 @@ class RadiosAdapter(val context: Context?, private val listener: OnRadioClickLis var native = false override fun onClick(view: View?) { - listener.onClick(this, data[layoutPosition]) + listener?.onClick(this, getRadioAt(layoutPosition)) } fun spin() { diff --git a/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt b/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt index 7be22da..f9a8090 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt @@ -12,7 +12,6 @@ import android.os.Build import android.os.IBinder import android.support.v4.media.session.MediaSessionCompat import android.view.KeyEvent -import com.github.apognu.otter.Otter import com.github.apognu.otter.R import com.github.apognu.otter.utils.* import com.google.android.exoplayer2.C diff --git a/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt b/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt index 151800d..ac823a1 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt @@ -6,6 +6,7 @@ import com.github.apognu.otter.repositories.FavoritedRepository import com.github.apognu.otter.repositories.Repository import com.github.apognu.otter.utils.* import com.github.kittinunf.fuel.Fuel +import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult import com.github.kittinunf.fuel.coroutines.awaitObjectResult import com.github.kittinunf.fuel.gson.gsonDeserializerOf import com.google.gson.Gson @@ -18,7 +19,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.withContext -data class RadioSessionBody(val radio_type: String, var custom_radio: Int? = null) +data class RadioSessionBody(val radio_type: String, var custom_radio: Int? = null, var related_object_id: String? = null) data class RadioSession(val id: Int) data class RadioTrackBody(val session: Int) data class RadioTrack(val position: Int, val track: RadioTrackID) @@ -29,6 +30,7 @@ class RadioPlayer(val context: Context) { private var currentRadio: Radio? = null private var session: Int? = null + private var cookie: String? = null private val favoritedRepository = FavoritedRepository(context) @@ -36,8 +38,11 @@ class RadioPlayer(val context: Context) { Cache.get(context, "radio_type")?.readLine()?.let { radio_type -> Cache.get(context, "radio_id")?.readLine()?.toInt()?.let { radio_id -> Cache.get(context, "radio_session")?.readLine()?.toInt()?.let { radio_session -> - currentRadio = Radio(radio_id, radio_type, "", "") - session = radio_session + Cache.get(context, "radio_cookie")?.readLine()?.let { radio_cookie -> + currentRadio = Radio(radio_id, radio_type, "", "") + session = radio_session + cookie = radio_cookie + } } } } @@ -59,6 +64,7 @@ class RadioPlayer(val context: Context) { Cache.delete(context, "radio_type") Cache.delete(context, "radio_id") Cache.delete(context, "radio_session") + Cache.delete(context, "radio_cookie") } fun isActive() = currentRadio != null && session != null @@ -66,24 +72,26 @@ class RadioPlayer(val context: Context) { private suspend fun createSession() { currentRadio?.let { radio -> try { - val request = RadioSessionBody(radio.radio_type).apply { + val request = RadioSessionBody(radio.radio_type, related_object_id = radio.related_object_id).apply { if (radio_type == "custom") { custom_radio = radio.id } } val body = Gson().toJson(request) - val result = Fuel.post(mustNormalizeUrl("/api/v1/radios/sessions/")) + val (_, response, result) = Fuel.post(mustNormalizeUrl("/api/v1/radios/sessions/")) .authorize() .header("Content-Type", "application/json") .body(body) - .awaitObjectResult(gsonDeserializerOf(RadioSession::class.java)) + .awaitObjectResponseResult(gsonDeserializerOf(RadioSession::class.java)) session = result.get().id + cookie = response.header("set-cookie").joinToString(";") Cache.set(context, "radio_type", radio.radio_type.toByteArray()) Cache.set(context, "radio_id", radio.id.toString().toByteArray()) Cache.set(context, "radio_session", session.toString().toByteArray()) + Cache.set(context, "radio_cookie", cookie.toString().toByteArray()) prepareNextTrack(true) } catch (e: Exception) { @@ -101,6 +109,11 @@ class RadioPlayer(val context: Context) { val result = Fuel.post(mustNormalizeUrl("/api/v1/radios/tracks/")) .authorize() .header("Content-Type", "application/json") + .apply { + cookie?.let { + header("cookie", it) + } + } .body(body) .awaitObjectResult(gsonDeserializerOf(RadioTrack::class.java)) diff --git a/app/src/main/java/com/github/apognu/otter/repositories/RadiosRepository.kt b/app/src/main/java/com/github/apognu/otter/repositories/RadiosRepository.kt index 2ed9dff..c7c1342 100644 --- a/app/src/main/java/com/github/apognu/otter/repositories/RadiosRepository.kt +++ b/app/src/main/java/com/github/apognu/otter/repositories/RadiosRepository.kt @@ -1,7 +1,6 @@ package com.github.apognu.otter.repositories import android.content.Context -import com.github.apognu.otter.R import com.github.apognu.otter.utils.FunkwhaleResponse import com.github.apognu.otter.utils.Radio import com.github.apognu.otter.utils.RadiosCache @@ -19,15 +18,7 @@ class RadiosRepository(override val context: Context?) : Repository): List { return data - .map { radio -> - radio.apply { radio_type = "custom" } - } + .map { radio -> radio.apply { radio_type = "custom" } } .toMutableList() - .apply { - context?.let { context -> - add(0, Radio(0, "random", context.getString(R.string.radio_random_title), context.getString(R.string.radio_random_description))) - add(1, Radio(0, "less-listened", context.getString(R.string.radio_less_listened_title), context.getString(R.string.radio_less_listened_description))) - } - } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/apognu/otter/utils/Models.kt b/app/src/main/java/com/github/apognu/otter/utils/Models.kt index 73b2846..052b603 100644 --- a/app/src/main/java/com/github/apognu/otter/utils/Models.kt +++ b/app/src/main/java/com/github/apognu/otter/utils/Models.kt @@ -3,6 +3,10 @@ package com.github.apognu.otter.utils import com.google.android.exoplayer2.offline.Download import com.preference.PowerPreference +data class User( + val full_username: String +) + sealed class CacheItem(val data: List) class ArtistsCache(data: List) : CacheItem(data) class AlbumsCache(data: List) : CacheItem(data) @@ -147,7 +151,8 @@ data class Radio( val id: Int, var radio_type: String, val name: String, - val description: String + val description: String, + var related_object_id: String? = null ) data class DownloadInfo( diff --git a/app/src/main/java/com/github/apognu/otter/utils/Userinfo.kt b/app/src/main/java/com/github/apognu/otter/utils/Userinfo.kt new file mode 100644 index 0000000..823a069 --- /dev/null +++ b/app/src/main/java/com/github/apognu/otter/utils/Userinfo.kt @@ -0,0 +1,34 @@ +package com.github.apognu.otter.utils + +import com.github.kittinunf.fuel.Fuel +import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult +import com.github.kittinunf.fuel.gson.gsonDeserializerOf +import com.github.kittinunf.result.Result +import com.preference.PowerPreference + +object Userinfo { + suspend fun get(): User? { + try { + val hostname = PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).getString("hostname") + val (_, _, result) = Fuel.get("$hostname/api/v1/users/users/me/") + .authorize() + .awaitObjectResponseResult(gsonDeserializerOf(User::class.java)) + + return when (result) { + is Result.Success -> { + val user = result.get() + + PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).apply { + setString("actor_username", user.full_username) + } + + user + } + + else -> null + } + } catch (e: Exception) { + return null + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/library.xml b/app/src/main/res/drawable/library.xml new file mode 100644 index 0000000..3de22fe --- /dev/null +++ b/app/src/main/res/drawable/library.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/row_radio_header.xml b/app/src/main/res/layout/row_radio_header.xml new file mode 100644 index 0000000..45892fe --- /dev/null +++ b/app/src/main/res/layout/row_radio_header.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a97f192..0838dd3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -10,6 +10,7 @@ Connexion Cela ne semble pas être un nom d\hôte valide Le nom d\'hôte Funkwhale devrait être sécurisé à travers HTTPS + Nous n\'avons pas pu récupérer les informations à propos de votre utilisateur Rechercher Téléchargements Paramètres @@ -88,6 +89,11 @@ Bitrate Instance Funkwhale Une erreur s\'est produite lors de la lecture de cette radio + Radios de l\'instance + Radios des utilisateurs + Votre contenu + Une sélection de votre propre bibliothèque. + Jouez vos morceaux favoris dans une boucle allègre infinie. Aléatoire Choix de pistes totalement aléatoires, vous découvrirez peut-être quelque chose ? Moins écoutées diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ca2cea3..14399e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -11,6 +11,7 @@ Logging in This could not be understood as a valid URL The Funkwhale hostname should be secure through HTTPS + We could not retrieve information about your user Search Downloads Settings @@ -89,6 +90,11 @@ Bitrate Funkwhale instance There was an error while trying to play this radio + Instance radios + User radios + Your content + Picks from your own libraries + Play your favorites tunes in a never-ending happiness loop. Random Totally random picks, maybe you\'ll discover new things? Less listened diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 92bbdf2..0dac914 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -93,4 +93,10 @@ @color/controlColor + +