#1573 Feed search favicon loading.

This commit is contained in:
sictiru 2021-12-22 17:39:44 -08:00
parent 5b50fe402a
commit 7329f54d62
8 changed files with 119 additions and 120 deletions

View file

@ -136,7 +136,7 @@
android:label="@string/mute_sites"/>
<activity
android:name=".activity.SearchForFeeds"
android:name=".activity.FeedSearchActivity"
android:launchMode="singleTop" />
<activity

View file

@ -19,7 +19,7 @@ import java.net.MalformedURLException
import java.net.URL
import java.util.*
class SearchForFeeds : NbActivity(), OnFeedSearchResultClickListener, AddFeedProgressListener {
class FeedSearchActivity : NbActivity(), OnFeedSearchResultClickListener, AddFeedProgressListener {
private val supportedUrlProtocols: MutableSet<String> = HashSet(2)
@ -40,7 +40,6 @@ class SearchForFeeds : NbActivity(), OnFeedSearchResultClickListener, AddFeedPro
setupListeners()
apiManager = APIManager(this)
binding.inputSearchQuery.requestFocus()
lifecycleScope
}
override fun onFeedSearchResultClickListener(result: FeedResult) {

View file

@ -0,0 +1,94 @@
package com.newsblur.activity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.newsblur.R
import com.newsblur.databinding.ViewFeedSearchRowBinding
import com.newsblur.domain.FeedResult
import com.newsblur.util.FeedUtils
class FeedSearchAdapter(
private val onClickListener: OnFeedSearchResultClickListener
) : RecyclerView.Adapter<FeedSearchAdapter.ViewHolder>() {
private val resultsList: MutableList<FeedResult> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.view_feed_search_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val result = resultsList[position]
holder.bind(result)
}
override fun getItemCount(): Int = resultsList.size
fun replaceAll(results: Array<FeedResult>) {
val newResultsList: List<FeedResult> = results.toList()
val diffCallback = ResultDiffCallback(resultsList, newResultsList)
val diffResult = DiffUtil.calculateDiff(diffCallback)
resultsList.clear()
resultsList.addAll(newResultsList)
diffResult.dispatchUpdatesTo(this)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val binding: ViewFeedSearchRowBinding = ViewFeedSearchRowBinding.bind(itemView)
fun bind(result: FeedResult) {
val resultFaviconUrl = result.faviconUrl
if (resultFaviconUrl.isNotEmpty()) {
FeedUtils.iconLoader?.displayImage(resultFaviconUrl, binding.imgFeedIcon)
}
binding.textTitle.text = result.label
binding.textTagline.text = result.tagline
val subscribersCountText = binding.root.context.getString(R.string.feed_subscribers, result.numberOfSubscriber)
binding.textSubscriptionCount.text = subscribersCountText
if (result.url.isNotEmpty()) {
binding.rowResultAddress.text = result.url
binding.rowResultAddress.visibility = View.VISIBLE
} else {
binding.rowResultAddress.visibility = View.GONE
}
itemView.setOnClickListener {
onClickListener.onFeedSearchResultClickListener(result)
}
}
}
class ResultDiffCallback(
private val oldList: List<FeedResult>,
private val newList: List<FeedResult>) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldFeedResult = oldList[oldItemPosition]
val newFeedResult = newList[newItemPosition]
return oldFeedResult == newFeedResult
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldFeedResult = oldList[oldItemPosition]
val newFeedResult = newList[newItemPosition]
return oldFeedResult.id == newFeedResult.id
&& oldFeedResult.label == newFeedResult.label
}
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
}
interface OnFeedSearchResultClickListener {
fun onFeedSearchResultClickListener(result: FeedResult)
}
}

View file

@ -1,94 +0,0 @@
package com.newsblur.activity
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.text.TextUtils
import android.util.Base64
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.newsblur.R
import com.newsblur.databinding.ViewFeedSearchRowBinding
import com.newsblur.domain.FeedResult
internal class FeedSearchAdapter(private val onClickListener: OnFeedSearchResultClickListener) : RecyclerView.Adapter<FeedSearchAdapter.ViewHolder>() {
private val resultsList: MutableList<FeedResult> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.view_feed_search_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val result = resultsList[position]
var bitmap: Bitmap? = null
if (!TextUtils.isEmpty(result.favicon)) {
val data = Base64.decode(result.favicon, Base64.DEFAULT)
bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
}
bitmap?.let {
holder.binding.imgFeedIcon.setImageBitmap(bitmap)
}
holder.binding.textTitle.text = result.label
holder.binding.textTagline.text = result.tagline
val subscribersCountText = holder.binding.root.context.resources.getString(R.string.feed_subscribers, result.numberOfSubscriber)
holder.binding.textSubscriptionCount.text = subscribersCountText
if (!TextUtils.isEmpty(result.url)) {
holder.binding.rowResultAddress.text = result.url
holder.binding.rowResultAddress.visibility = View.VISIBLE
} else {
holder.binding.rowResultAddress.visibility = View.GONE
}
holder.itemView.setOnClickListener {
onClickListener.onFeedSearchResultClickListener(result)
}
}
override fun getItemCount(): Int = resultsList.size
fun replaceAll(results: Array<FeedResult>) {
val newResultsList: List<FeedResult> = results.toList()
val diffCallback = ResultDiffCallback(resultsList, newResultsList)
val diffResult = DiffUtil.calculateDiff(diffCallback)
resultsList.clear()
resultsList.addAll(newResultsList)
diffResult.dispatchUpdatesTo(this)
}
internal class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding: ViewFeedSearchRowBinding = ViewFeedSearchRowBinding.bind(itemView)
}
internal class ResultDiffCallback(private val oldList: List<FeedResult>,
private val newList: List<FeedResult>) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldFeedResult = oldList[oldItemPosition]
val newFeedResult = newList[newItemPosition]
return oldFeedResult.label == newFeedResult.label &&
oldFeedResult.numberOfSubscriber == newFeedResult.numberOfSubscriber
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldFeedResult = oldList[oldItemPosition]
val newFeedResult = newList[newItemPosition]
return oldFeedResult.label == newFeedResult.label
&& oldFeedResult.tagline == newFeedResult.tagline
}
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
}
interface OnFeedSearchResultClickListener {
fun onFeedSearchResultClickListener(result: FeedResult)
}
}

View file

@ -340,7 +340,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
}
private void onClickAddButton() {
Intent i = new Intent(this, SearchForFeeds.class);
Intent i = new Intent(this, FeedSearchActivity.class);
startActivity(i);
}

View file

@ -1,22 +0,0 @@
package com.newsblur.domain;
import com.google.gson.annotations.SerializedName;
public class FeedResult {
@SerializedName("num_subscribers")
public int numberOfSubscriber;
@SerializedName("favicon_color")
public String faviconColor;
@SerializedName("value")
public String url;
public String tagline;
public String label;
public String favicon;
}

View file

@ -0,0 +1,21 @@
package com.newsblur.domain
import com.google.gson.annotations.SerializedName
import com.newsblur.network.APIConstants
data class FeedResult(
@SerializedName("id")
val id: Int = 0,
@SerializedName("tagline")
val tagline: String? = null,
@SerializedName("label")
val label: String,
@SerializedName("num_subscribers")
val numberOfSubscriber: Int = 0,
@SerializedName("value")
val url: String,
) {
val faviconUrl: String
get() = "${APIConstants.buildUrl(APIConstants.PATH_FEED_FAVICON_URL)}$id"
}

View file

@ -81,6 +81,7 @@ public class APIConstants {
public static final String PATH_RENAME_FOLDER = "/reader/rename_folder";
public static final String PATH_SAVE_RECEIPT = "/profile/save_android_receipt";
public static final String PATH_FEED_STATISTICS = "/rss_feeds/statistics_embedded/";
public static final String PATH_FEED_FAVICON_URL = "/rss_feeds/icon/";
public static String buildUrl(String path) {
return CurrentUrlBase + path;