mirror of
https://github.com/apognu/otter
synced 2025-02-11 00:30:35 +01:00
Fixed download and cache indicators on search screen. Fixed an issue with placeholder texts when some search terms did not return results.
This commit is contained in:
parent
08a7a28c22
commit
03fcf1a382
@ -4,6 +4,7 @@ import android.os.Bundle
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.apognu.otter.Otter
|
||||
import com.github.apognu.otter.R
|
||||
import com.github.apognu.otter.adapters.DownloadsAdapter
|
||||
import com.github.apognu.otter.utils.*
|
||||
@ -45,21 +46,21 @@ class DownloadsActivity : AppCompatActivity() {
|
||||
|
||||
private fun refresh() {
|
||||
GlobalScope.launch(Main) {
|
||||
RequestBus.send(Request.GetDownloads).wait<Response.Downloads>()?.let { response ->
|
||||
adapter.downloads.clear()
|
||||
val cursor = Otter.get().exoDownloadManager.downloadIndex.getDownloads()
|
||||
|
||||
while (response.cursor.moveToNext()) {
|
||||
val download = response.cursor.download
|
||||
adapter.downloads.clear()
|
||||
|
||||
download.getMetadata()?.let { info ->
|
||||
adapter.downloads.add(info.apply {
|
||||
this.download = download
|
||||
})
|
||||
}
|
||||
while (cursor.moveToNext()) {
|
||||
val download = cursor.download
|
||||
|
||||
download.getMetadata()?.let { info ->
|
||||
adapter.downloads.add(info.apply {
|
||||
this.download = download
|
||||
})
|
||||
}
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,8 @@ class SearchActivity : AppCompatActivity() {
|
||||
|
||||
lateinit var favoritesRepository: FavoritesRepository
|
||||
|
||||
var done = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@ -46,6 +48,8 @@ class SearchActivity : AppCompatActivity() {
|
||||
search.clearFocus()
|
||||
|
||||
rawQuery?.let {
|
||||
done = 0
|
||||
|
||||
val query = URLEncoder.encode(it, "UTF-8")
|
||||
|
||||
tracksRepository = TracksSearchRepository(this@SearchActivity, query.toLowerCase(Locale.ROOT))
|
||||
@ -54,6 +58,7 @@ class SearchActivity : AppCompatActivity() {
|
||||
favoritesRepository = FavoritesRepository(this@SearchActivity)
|
||||
|
||||
search_spinner.visibility = View.VISIBLE
|
||||
search_empty.visibility = View.GONE
|
||||
search_no_results.visibility = View.GONE
|
||||
|
||||
adapter.artists.clear()
|
||||
@ -62,33 +67,24 @@ class SearchActivity : AppCompatActivity() {
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
artistsRepository.fetch(Repository.Origin.Network.origin).untilNetwork { artists, _, _ ->
|
||||
when (artists.isEmpty()) {
|
||||
true -> search_no_results.visibility = View.VISIBLE
|
||||
false -> adapter.artists.addAll(artists)
|
||||
}
|
||||
done++
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
adapter.artists.addAll(artists)
|
||||
refresh()
|
||||
}
|
||||
|
||||
albumsRepository.fetch(Repository.Origin.Network.origin).untilNetwork { albums, _, _ ->
|
||||
when (albums.isEmpty()) {
|
||||
true -> search_no_results.visibility = View.VISIBLE
|
||||
false -> adapter.albums.addAll(albums)
|
||||
}
|
||||
done++
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
adapter.albums.addAll(albums)
|
||||
refresh()
|
||||
}
|
||||
|
||||
tracksRepository.fetch(Repository.Origin.Network.origin).untilNetwork { tracks, _, _ ->
|
||||
search_spinner.visibility = View.GONE
|
||||
search_empty.visibility = View.GONE
|
||||
done++
|
||||
|
||||
when (tracks.isEmpty()) {
|
||||
true -> search_no_results.visibility = View.VISIBLE
|
||||
false -> adapter.tracks.addAll(tracks)
|
||||
}
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
adapter.tracks.addAll(tracks)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +95,20 @@ class SearchActivity : AppCompatActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
if (adapter.artists.size + adapter.albums.size + adapter.tracks.size == 0) {
|
||||
search_no_results.visibility = View.VISIBLE
|
||||
} else {
|
||||
search_no_results.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (done == 3) {
|
||||
search_spinner.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
inner class SearchResultClickListener : SearchAdapter.OnSearchResultClickListener {
|
||||
override fun onArtistClick(holder: View?, artist: Artist) {
|
||||
ArtistsFragment.openAlbums(this@SearchActivity, artist)
|
||||
|
@ -2,6 +2,8 @@ package com.github.apognu.otter.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
@ -69,10 +71,6 @@ class SearchAdapter(private val context: Context?, private val listener: OnSearc
|
||||
return ResultType.Track.ordinal
|
||||
}
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = when (viewType) {
|
||||
ResultType.Header.ordinal -> LayoutInflater.from(context).inflate(R.layout.row_search_header, parent, false)
|
||||
@ -93,27 +91,33 @@ class SearchAdapter(private val context: Context?, private val listener: OnSearc
|
||||
if (position == 0) {
|
||||
holder.title.text = context.getString(R.string.artists)
|
||||
holder.itemView.visibility = View.VISIBLE
|
||||
holder.itemView.layoutParams = RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
if (artists.isEmpty()) {
|
||||
holder.itemView.visibility = View.GONE
|
||||
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (position == (artists.size + 1)) {
|
||||
holder.title.text = context.getString(R.string.albums)
|
||||
holder.itemView.visibility = View.VISIBLE
|
||||
holder.itemView.layoutParams = RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
if (albums.isEmpty()) {
|
||||
holder.itemView.visibility = View.GONE
|
||||
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (position == (artists.size + albums.size + 2)) {
|
||||
holder.title.text = context.getString(R.string.tracks)
|
||||
holder.itemView.visibility = View.VISIBLE
|
||||
holder.itemView.layoutParams = RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
if (tracks.isEmpty()) {
|
||||
holder.itemView.visibility = View.GONE
|
||||
holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,6 +164,8 @@ class SearchAdapter(private val context: Context?, private val listener: OnSearc
|
||||
holder.artist.typeface = Typeface.create(holder.artist.typeface, Typeface.NORMAL)
|
||||
})
|
||||
|
||||
holder.title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
|
||||
|
||||
if (resultType == ResultType.Track.ordinal) {
|
||||
(item as? Track)?.let { track ->
|
||||
context?.let { context ->
|
||||
@ -183,6 +189,23 @@ class SearchAdapter(private val context: Context?, private val listener: OnSearc
|
||||
}
|
||||
}
|
||||
|
||||
when (track.cached || track.downloaded) {
|
||||
true -> holder.title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.downloaded, 0, 0, 0)
|
||||
false -> holder.title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
if (track.cached && !track.downloaded) {
|
||||
holder.title.compoundDrawables.forEach {
|
||||
it?.colorFilter = PorterDuffColorFilter(context.getColor(R.color.cached), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
|
||||
if (track.downloaded) {
|
||||
holder.title.compoundDrawables.forEach {
|
||||
it?.colorFilter = PorterDuffColorFilter(context.getColor(R.color.downloaded), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
|
||||
holder.actions.setOnClickListener {
|
||||
PopupMenu(context, holder.actions, Gravity.START, R.attr.actionOverflowMenuStyle, 0).apply {
|
||||
inflate(R.menu.row_track)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.github.apognu.otter.repositories
|
||||
|
||||
import android.content.Context
|
||||
import com.github.apognu.otter.Otter
|
||||
import com.github.apognu.otter.utils.*
|
||||
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
|
||||
import com.google.gson.reflect.TypeToken
|
||||
@ -22,8 +23,18 @@ class TracksSearchRepository(override val context: Context?, query: String) : Re
|
||||
.toList()
|
||||
.flatten()
|
||||
|
||||
val downloaded = TracksRepository.getDownloadedIds() ?: listOf()
|
||||
|
||||
data.map { track ->
|
||||
track.favorite = favorites.contains(track.id)
|
||||
track.downloaded = downloaded.contains(track.id)
|
||||
|
||||
track.bestUpload()?.let { upload ->
|
||||
val url = mustNormalizeUrl(upload.listen_url)
|
||||
|
||||
track.cached = Otter.get().exoCache.isCached(url, 0, upload.duration * 1000L)
|
||||
}
|
||||
|
||||
track
|
||||
}
|
||||
}
|
||||
|
@ -19,22 +19,21 @@ class TracksRepository(override val context: Context?, albumId: Int) : Repositor
|
||||
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(TracksCache::class.java).deserialize(reader)
|
||||
|
||||
companion object {
|
||||
suspend fun getDownloadedIds(): List<Int>? {
|
||||
return RequestBus.send(Request.GetDownloads).wait<com.github.apognu.otter.utils.Response.Downloads>()?.let { response ->
|
||||
val ids: MutableList<Int> = mutableListOf()
|
||||
fun getDownloadedIds(): List<Int>? {
|
||||
val cursor = Otter.get().exoDownloadManager.downloadIndex.getDownloads()
|
||||
val ids: MutableList<Int> = mutableListOf()
|
||||
|
||||
while (response.cursor.moveToNext()) {
|
||||
val download = response.cursor.download
|
||||
while (cursor.moveToNext()) {
|
||||
val download = cursor.download
|
||||
|
||||
download.getMetadata()?.let {
|
||||
if (download.state == Download.STATE_COMPLETED) {
|
||||
ids.add(it.id)
|
||||
}
|
||||
download.getMetadata()?.let {
|
||||
if (download.state == Download.STATE_COMPLETED) {
|
||||
ids.add(it.id)
|
||||
}
|
||||
}
|
||||
|
||||
ids
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import com.github.apognu.otter.Otter
|
||||
import com.google.android.exoplayer2.offline.Download
|
||||
import com.google.android.exoplayer2.offline.DownloadCursor
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
@ -89,7 +88,7 @@ object CommandBus {
|
||||
object RequestBus {
|
||||
fun send(request: Request): Channel<Response> {
|
||||
return Channel<Response>().also {
|
||||
GlobalScope.launch(Main) {
|
||||
GlobalScope.launch(IO) {
|
||||
request.channel = it
|
||||
|
||||
Otter.get().requestBus.offer(request)
|
||||
|
@ -18,28 +18,39 @@
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:elevation="4dp">
|
||||
|
||||
<androidx.appcompat.widget.SearchView
|
||||
android:id="@+id/search"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconifiedByDefault="false"
|
||||
app:queryBackground="@android:color/transparent"
|
||||
app:queryHint="@string/search_placeholder" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.SearchView
|
||||
android:id="@+id/search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconifiedByDefault="false"
|
||||
app:queryBackground="@android:color/transparent"
|
||||
app:queryHint="@string/search_placeholder" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/search_spinner"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-12dp"
|
||||
android:layout_marginBottom="-12dp"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/search_spinner"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/search_empty"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -3,7 +3,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:background="@drawable/ripple"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:transitionGroup="true"
|
||||
|
Loading…
x
Reference in New Issue
Block a user