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:
Antoine POPINEAU 2020-06-22 21:48:31 +02:00
parent 08a7a28c22
commit 03fcf1a382
No known key found for this signature in database
GPG Key ID: A78AC64694F84063
8 changed files with 116 additions and 62 deletions

View File

@ -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()
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)

View File

@ -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"

View File

@ -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"