[WIP] Replace Fresco with Coil

Fresco is replaced with Coil verywhere. But Coil is not used directly to
avoid splintering the dependency everywhere in the code. Coil is wrapped
by extension functions for 'ImageView'.

Some shared functionality is moved from 'DisplayUtils' into the
'ImageViewExtensions'.

Resolves: #2227, #2376

Signed-off-by: Tim Krüger <t@timkrueger.me>
This commit is contained in:
Tim Krüger 2022-09-29 13:19:58 +02:00
parent b2d6211b3c
commit 863052b53e
No known key found for this signature in database
GPG key ID: FECE3A7222C52A4E
6 changed files with 110 additions and 62 deletions

View file

@ -37,20 +37,18 @@ import android.text.format.DateUtils
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.interfaces.DraweeController
import com.nextcloud.talk.R
import com.nextcloud.talk.adapters.items.ConversationItem.ConversationItemViewHolder
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemConversationWithLastMessageBinding
import com.nextcloud.talk.extensions.loadAvatar
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType
import com.nextcloud.talk.models.json.status.Status
import com.nextcloud.talk.ui.StatusDrawable
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -116,7 +114,6 @@ class ConversationItem(
payloads: List<Any>
) {
val appContext = sharedApplication!!.applicationContext
holder.binding.dialogAvatar.controller = null
holder.binding.dialogName.setTextColor(
ResourcesCompat.getColor(
context.resources,
@ -278,32 +275,16 @@ class ConversationItem(
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
val layerDrawable = LayerDrawable(layers)
holder.binding.dialogAvatar.hierarchy.setPlaceholderImage(
DisplayUtils.getRoundedDrawable(layerDrawable)
)
holder.binding.dialogAvatar.loadAvatar(DisplayUtils.getRoundedDrawable(layerDrawable))
} else {
holder.binding.dialogAvatar.hierarchy.setPlaceholderImage(R.mipmap.ic_launcher)
holder.binding.dialogAvatar.loadAvatar(R.mipmap.ic_launcher)
}
shouldLoadAvatar = false
}
if (shouldLoadAvatar) {
when (model.type) {
ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(model.name)) {
val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
.setOldController(holder.binding.dialogAvatar.controller)
.setAutoPlayAnimations(true)
.setImageRequest(
DisplayUtils.getImageRequestForUrl(
ApiUtils.getUrlForAvatar(
user.baseUrl,
model.name,
true
),
user
)
)
.build()
holder.binding.dialogAvatar.controller = draweeController
holder.binding.dialogAvatar.loadAvatar(user, model.name!!)
} else {
holder.binding.dialogAvatar.visibility = View.GONE
}

View file

@ -2,6 +2,8 @@
* Nextcloud Talk application
*
* @author Álvaro Brey
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
@ -27,9 +29,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemSearchMessageBinding
import com.nextcloud.talk.extensions.loadThumbnail
import com.nextcloud.talk.models.domain.SearchMessageEntry
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFilterable
@ -72,7 +74,7 @@ data class MessageResultItem constructor(
) {
holder.binding.conversationTitle.text = messageEntry.title
bindMessageExcerpt(holder)
loadImage(holder)
holder.binding.thumbnail.loadThumbnail(messageEntry.thumbnailURL, currentUser)
}
private fun bindMessageExcerpt(holder: ViewHolder) {
@ -83,17 +85,6 @@ data class MessageResultItem constructor(
)
}
private fun loadImage(holder: ViewHolder) {
DisplayUtils.loadAvatarPlaceholder(holder.binding.thumbnail)
if (messageEntry.thumbnailURL != null) {
val imageRequest = DisplayUtils.getImageRequestForUrl(
messageEntry.thumbnailURL,
currentUser
)
DisplayUtils.loadImage(holder.binding.thumbnail, imageRequest)
}
}
override fun filter(constraint: String?): Boolean = true
override fun getItemViewType(): Int {

View file

@ -0,0 +1,98 @@
/*
* Nextcloud Talk application
*
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.extensions
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.os.Build
import android.widget.ImageView
import androidx.core.content.ContextCompat
import coil.load
import coil.request.ImageRequest
import coil.transform.CircleCropTransformation
import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.utils.ApiUtils
fun ImageView.loadAvatar(user: User, avatar: String): io.reactivex.disposables.Disposable {
val imageRequestUri = ApiUtils.getUrlForAvatar(
user.baseUrl,
avatar,
true
)
return DisposableWrapper(
load(imageRequestUri) {
addHeader(
"Authorization",
ApiUtils.getCredentials(user.username, user.token)
)
transformations(CircleCropTransformation())
}
)
}
fun ImageView.loadThumbnail(url: String?, user: User): io.reactivex.disposables.Disposable {
val requestBuilder = ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.target(this)
.transformations(CircleCropTransformation())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val layers = arrayOfNulls<Drawable>(2)
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
requestBuilder.placeholder(LayerDrawable(layers))
} else {
requestBuilder.placeholder(R.mipmap.ic_launcher)
}
if (url != null &&
url.startsWith(user.baseUrl!!) &&
(url.contains("index.php/core/preview?fileId=") || url.contains("/avatar/"))
) {
requestBuilder.addHeader(
"Authorization",
ApiUtils.getCredentials(user.username, user.token)
)
}
return DisposableWrapper(load(requestBuilder.build()))
}
fun ImageView.loadAvatar(any: Any?): io.reactivex.disposables.Disposable {
return DisposableWrapper(load(any))
}
private class DisposableWrapper(private val disposable: coil.request.Disposable) : io.reactivex.disposables
.Disposable {
override fun dispose() {
disposable.dispose()
}
override fun isDisposed(): Boolean {
return disposable.isDisposed
}
}

View file

@ -617,30 +617,6 @@ public class DisplayUtils {
avatarImageView.setController(draweeController);
}
public static void loadAvatarPlaceholder(final SimpleDraweeView targetView) {
final Context context = targetView.getContext();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Drawable[] layers = new Drawable[2];
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background);
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground);
LayerDrawable layerDrawable = new LayerDrawable(layers);
targetView.getHierarchy().setPlaceholderImage(
DisplayUtils.getRoundedDrawable(layerDrawable));
} else {
targetView.getHierarchy().setPlaceholderImage(R.mipmap.ic_launcher);
}
}
public static void loadImage(final SimpleDraweeView targetView, final ImageRequest imageRequest) {
final DraweeController newController = Fresco.newDraweeControllerBuilder()
.setOldController(targetView.getController())
.setAutoPlayAnimations(true)
.setImageRequest(imageRequest)
.build();
targetView.setController(newController);
}
public static @StringRes
int getSortOrderStringId(FileSortOrder sortOrder) {
switch (sortOrder.getName()) {

View file

@ -38,7 +38,7 @@
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/double_margin_between_elements">
<com.facebook.drawee.view.SimpleDraweeView
<ImageView
android:id="@id/dialogAvatar"
android:layout_width="@dimen/small_item_height"
android:layout_height="@dimen/small_item_height"

View file

@ -3,6 +3,8 @@
~
~ @author Mario Danic
~ @author Andy Scherzinger
~ @author Tim Krüger
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
@ -35,7 +37,7 @@
android:layout_margin="@dimen/double_margin_between_elements"
tools:background="@color/white">
<com.facebook.drawee.view.SimpleDraweeView
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/small_item_height"
android:layout_height="@dimen/small_item_height"