Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/talk-android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/build.gradle2
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java371
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt352
-rw-r--r--app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt17
-rw-r--r--app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt12
-rw-r--r--app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt98
-rw-r--r--app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java60
-rw-r--r--app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt6
-rw-r--r--app/src/main/res/layout/rv_item_conversation_with_last_message.xml2
-rw-r--r--app/src/main/res/layout/rv_item_search_message.xml4
-rw-r--r--scripts/analysis/findbugs-results.txt2
11 files changed, 435 insertions, 491 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 22dd9ab25..5cb2e675a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -42,7 +42,7 @@ android {
namespace 'com.nextcloud.talk'
defaultConfig {
- minSdkVersion 23
+ minSdkVersion 21
targetSdkVersion 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java
new file mode 100644
index 000000000..7110277df
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java
@@ -0,0 +1,371 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * @author Andy Scherzinger
+ * @author Marcel Hibbe
+ * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
+ * Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
+ * Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.adapters.items;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.view.View;
+
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.drawee.interfaces.DraweeController;
+import com.nextcloud.talk.R;
+import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.data.user.model.User;
+import com.nextcloud.talk.databinding.RvItemConversationWithLastMessageBinding;
+import com.nextcloud.talk.models.json.chat.ChatMessage;
+import com.nextcloud.talk.models.json.conversations.Conversation;
+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;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+import androidx.core.content.ContextCompat;
+import androidx.core.content.res.ResourcesCompat;
+import eu.davidea.flexibleadapter.FlexibleAdapter;
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
+import eu.davidea.flexibleadapter.items.IFilterable;
+import eu.davidea.flexibleadapter.items.IFlexible;
+import eu.davidea.flexibleadapter.items.ISectionable;
+import eu.davidea.viewholders.FlexibleViewHolder;
+
+public class ConversationItem extends AbstractFlexibleItem<ConversationItem.ConversationItemViewHolder> implements
+ ISectionable<ConversationItem.ConversationItemViewHolder, GenericTextHeaderItem>, IFilterable<String> {
+
+ public static final int VIEW_TYPE = R.layout.rv_item_conversation_with_last_message;
+
+ private static final float STATUS_SIZE_IN_DP = 9f;
+
+ private final Conversation conversation;
+ private final User user;
+ private final Context context;
+ private GenericTextHeaderItem header;
+ private final Status status;
+ private final ViewThemeUtils viewThemeUtils;
+
+
+ public ConversationItem(Conversation conversation, User user, Context activityContext, Status status, final ViewThemeUtils viewThemeUtils) {
+ this.conversation = conversation;
+ this.user = user;
+ this.context = activityContext;
+ this.status = status;
+ this.viewThemeUtils = viewThemeUtils;
+ }
+
+ public ConversationItem(Conversation conversation, User user,
+ Context activityContext, GenericTextHeaderItem genericTextHeaderItem, Status status,
+ final ViewThemeUtils viewThemeUtils) {
+ this(conversation, user, activityContext, status, viewThemeUtils);
+ this.header = genericTextHeaderItem;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ConversationItem) {
+ ConversationItem inItem = (ConversationItem) o;
+ return conversation.equals(inItem.getModel()) && Objects.equals(status, inItem.status);
+ }
+ return false;
+ }
+
+ public Conversation getModel() {
+ return conversation;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = conversation.hashCode();
+ result = 31 * result + (status != null ? status.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public int getLayoutRes() {
+ return R.layout.rv_item_conversation_with_last_message;
+ }
+
+ @Override
+ public int getItemViewType() {
+ return VIEW_TYPE;
+ }
+
+ @Override
+ public ConversationItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
+ return new ConversationItemViewHolder(view, adapter);
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void bindViewHolder(FlexibleAdapter<IFlexible> adapter,
+ ConversationItemViewHolder holder,
+ int position,
+ List<Object> payloads) {
+ Context appContext =
+ NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext();
+ holder.binding.dialogAvatar.setController(null);
+
+ holder.binding.dialogName.setTextColor(ResourcesCompat.getColor(context.getResources(),
+ R.color.conversation_item_header,
+ null));
+
+ if (adapter.hasFilter()) {
+ viewThemeUtils.platform.highlightText(holder.binding.dialogName,
+ conversation.getDisplayName(),
+ String.valueOf(adapter.getFilter(String.class)));
+ } else {
+ holder.binding.dialogName.setText(conversation.getDisplayName());
+ }
+
+ if (conversation.getUnreadMessages() > 0) {
+ holder.binding.dialogName.setTypeface(holder.binding.dialogName.getTypeface(), Typeface.BOLD);
+ holder.binding.dialogLastMessage.setTypeface(holder.binding.dialogLastMessage.getTypeface(), Typeface.BOLD);
+ holder.binding.dialogUnreadBubble.setVisibility(View.VISIBLE);
+ if (conversation.getUnreadMessages() < 1000) {
+ holder.binding.dialogUnreadBubble.setText(Long.toString(conversation.getUnreadMessages()));
+ } else {
+ holder.binding.dialogUnreadBubble.setText(R.string.tooManyUnreadMessages);
+ }
+
+ ColorStateList lightBubbleFillColor = ColorStateList.valueOf(
+ ContextCompat.getColor(context,
+ R.color.conversation_unread_bubble));
+ int lightBubbleTextColor = ContextCompat.getColor(
+ context,
+ R.color.conversation_unread_bubble_text);
+
+ if (conversation.getType() == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
+ viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble);
+ } else if (conversation.getUnreadMention()) {
+ if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "direct-mention-flag")) {
+ if (conversation.getUnreadMentionDirect()) {
+ viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble);
+ } else {
+ viewThemeUtils.material.colorChipOutlined(holder.binding.dialogUnreadBubble, 6.0f);
+ }
+ } else {
+ viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble);
+ }
+ } else {
+ holder.binding.dialogUnreadBubble.setChipBackgroundColor(lightBubbleFillColor);
+ holder.binding.dialogUnreadBubble.setTextColor(lightBubbleTextColor);
+ }
+ } else {
+ holder.binding.dialogName.setTypeface(null, Typeface.NORMAL);
+ holder.binding.dialogDate.setTypeface(null, Typeface.NORMAL);
+ holder.binding.dialogLastMessage.setTypeface(null, Typeface.NORMAL);
+ holder.binding.dialogUnreadBubble.setVisibility(View.GONE);
+ }
+
+ if (conversation.getFavorite()) {
+ holder.binding.favoriteConversationImageView.setVisibility(View.VISIBLE);
+ } else {
+ holder.binding.favoriteConversationImageView.setVisibility(View.GONE);
+ }
+
+ if (status != null && Conversation.ConversationType.ROOM_SYSTEM != conversation.getType()) {
+ float size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, appContext);
+
+ holder.binding.userStatusImage.setVisibility(View.VISIBLE);
+ holder.binding.userStatusImage.setImageDrawable(new StatusDrawable(
+ status.getStatus(),
+ status.getIcon(),
+ size,
+ context.getResources().getColor(R.color.bg_default),
+ appContext));
+ } else {
+ holder.binding.userStatusImage.setVisibility(View.GONE);
+ }
+
+ if (conversation.getLastMessage() != null) {
+ holder.binding.dialogDate.setVisibility(View.VISIBLE);
+ holder.binding.dialogDate.setText(
+ DateUtils.getRelativeTimeSpanString(conversation.getLastActivity() * 1000L,
+ System.currentTimeMillis(),
+ 0,
+ DateUtils.FORMAT_ABBREV_RELATIVE));
+
+ if (!TextUtils.isEmpty(conversation.getLastMessage().getSystemMessage()) ||
+ Conversation.ConversationType.ROOM_SYSTEM == conversation.getType()) {
+ holder.binding.dialogLastMessage.setText(conversation.getLastMessage().getText());
+ } else {
+ String authorDisplayName = "";
+ conversation.getLastMessage().setActiveUser(user);
+ String text;
+ if (conversation.getLastMessage().getCalculateMessageType() == ChatMessage.MessageType.REGULAR_TEXT_MESSAGE) {
+ if (conversation.getLastMessage().getActorId().equals(user.getUserId())) {
+ text = String.format(appContext.getString(R.string.nc_formatted_message_you),
+ conversation.getLastMessage().getLastMessageDisplayText());
+ } else {
+ authorDisplayName = !TextUtils.isEmpty(conversation.getLastMessage().getActorDisplayName()) ?
+ conversation.getLastMessage().getActorDisplayName() :
+ "guests".equals(conversation.getLastMessage().getActorType()) ?
+ appContext.getString(R.string.nc_guest) : "";
+ text = String.format(appContext.getString(R.string.nc_formatted_message),
+ authorDisplayName,
+ conversation.getLastMessage().getLastMessageDisplayText());
+ }
+ } else {
+ text = conversation.getLastMessage().getLastMessageDisplayText();
+ }
+
+ holder.binding.dialogLastMessage.setText(text);
+ }
+ } else {
+ holder.binding.dialogDate.setVisibility(View.GONE);
+ holder.binding.dialogLastMessage.setText(R.string.nc_no_messages_yet);
+ }
+
+ holder.binding.dialogAvatar.setVisibility(View.VISIBLE);
+
+ boolean shouldLoadAvatar = true;
+ String objectType;
+ if (!TextUtils.isEmpty(objectType = conversation.getObjectType())) {
+ switch (objectType) {
+ case "share:password":
+ shouldLoadAvatar = false;
+ holder.binding.dialogAvatar.setImageDrawable(
+ ContextCompat.getDrawable(context,
+ R.drawable.ic_circular_lock));
+ break;
+ case "file":
+ shouldLoadAvatar = false;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ holder.binding.dialogAvatar.setImageDrawable(
+ DisplayUtils.getRoundedDrawable(
+ viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
+ R.drawable.ic_avatar_document)));
+ } else {
+ holder.binding.dialogAvatar.setImageDrawable(
+ ContextCompat.getDrawable(context, R.drawable.ic_circular_document));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (Conversation.ConversationType.ROOM_SYSTEM.equals(conversation.getType())) {
+ 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);
+
+ holder.binding.dialogAvatar.getHierarchy().setPlaceholderImage(
+ DisplayUtils.getRoundedDrawable(layerDrawable));
+ } else {
+ holder.binding.dialogAvatar.getHierarchy().setPlaceholderImage(R.mipmap.ic_launcher);
+ }
+ shouldLoadAvatar = false;
+ }
+
+ if (shouldLoadAvatar) {
+ switch (conversation.getType()) {
+ case ROOM_TYPE_ONE_TO_ONE_CALL:
+ if (!TextUtils.isEmpty(conversation.getName())) {
+ DraweeController draweeController = Fresco.newDraweeControllerBuilder()
+ .setOldController(holder.binding.dialogAvatar.getController())
+ .setAutoPlayAnimations(true)
+ .setImageRequest(DisplayUtils.getImageRequestForUrl(
+ ApiUtils.getUrlForAvatar(user.getBaseUrl(),
+ conversation.getName(),
+ true),
+ user))
+ .build();
+ holder.binding.dialogAvatar.setController(draweeController);
+ } else {
+ holder.binding.dialogAvatar.setVisibility(View.GONE);
+ }
+ break;
+ case ROOM_GROUP_CALL:
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ holder.binding.dialogAvatar.setImageDrawable(
+ DisplayUtils.getRoundedDrawable(
+ viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
+ R.drawable.ic_avatar_group)));
+ } else {
+ holder.binding.dialogAvatar.setImageDrawable(
+ ContextCompat.getDrawable(context, R.drawable.ic_circular_group));
+ }
+ break;
+ case ROOM_PUBLIC_CALL:
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ holder.binding.dialogAvatar.setImageDrawable(
+ DisplayUtils.getRoundedDrawable(
+ viewThemeUtils.talk.themePlaceholderAvatar(holder.binding.dialogAvatar,
+ R.drawable.ic_avatar_link)));
+ } else {
+ holder.binding.dialogAvatar.setImageDrawable(
+ ContextCompat.getDrawable(context, R.drawable.ic_circular_link));
+ }
+ break;
+ default:
+ holder.binding.dialogAvatar.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @Override
+ public boolean filter(String constraint) {
+ return conversation.getDisplayName() != null &&
+ Pattern
+ .compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL)
+ .matcher(conversation.getDisplayName().trim())
+ .find();
+ }
+
+ @Override
+ public GenericTextHeaderItem getHeader() {
+ return header;
+ }
+
+ @Override
+ public void setHeader(GenericTextHeaderItem header) {
+ this.header = header;
+ }
+
+ static class ConversationItemViewHolder extends FlexibleViewHolder {
+
+ RvItemConversationWithLastMessageBinding binding;
+
+ ConversationItemViewHolder(View view, FlexibleAdapter adapter) {
+ super(view, adapter);
+ binding = RvItemConversationWithLastMessageBinding.bind(view);
+ }
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt
deleted file mode 100644
index 576ddccae..000000000
--- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Nextcloud Talk application
- *
- * @author Mario Danic
- * @author Andy Scherzinger
- * @author Marcel Hibbe
- * @author Tim Krüger
- * Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
- * Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
- * Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
- * Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-package com.nextcloud.talk.adapters.items
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Typeface
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.LayerDrawable
-import android.os.Build
-import android.text.TextUtils
-import android.text.format.DateUtils
-import android.view.View
-import androidx.core.content.ContextCompat
-import androidx.core.content.res.ResourcesCompat
-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.DisplayUtils
-import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability
-import eu.davidea.flexibleadapter.FlexibleAdapter
-import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
-import eu.davidea.flexibleadapter.items.IFilterable
-import eu.davidea.flexibleadapter.items.IFlexible
-import eu.davidea.flexibleadapter.items.ISectionable
-import eu.davidea.viewholders.FlexibleViewHolder
-import java.util.regex.Pattern
-
-class ConversationItem(
- val model: Conversation,
- private val user: User,
- private val context: Context,
- private val status: Status?,
- private val viewThemeUtils: ViewThemeUtils
-) : AbstractFlexibleItem<ConversationItemViewHolder>(),
- ISectionable<ConversationItemViewHolder, GenericTextHeaderItem?>,
- IFilterable<String?> {
- private var header: GenericTextHeaderItem? = null
-
- constructor(
- conversation: Conversation,
- user: User,
- activityContext: Context,
- genericTextHeaderItem: GenericTextHeaderItem?,
- status: Status?,
- viewThemeUtils: ViewThemeUtils
- ) : this(conversation, user, activityContext, status, viewThemeUtils) {
- header = genericTextHeaderItem
- }
-
- override fun equals(other: Any?): Boolean {
- if (other is ConversationItem) {
- return model == other.model && status == other.status
- }
- return false
- }
-
- override fun hashCode(): Int {
- var result = model.hashCode()
- result = 31 * result + (status?.hashCode() ?: 0)
- return result
- }
-
- override fun getLayoutRes(): Int {
- return R.layout.rv_item_conversation_with_last_message
- }
-
- override fun getItemViewType(): Int {
- return VIEW_TYPE
- }
-
- override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>?>?): ConversationItemViewHolder {
- return ConversationItemViewHolder(view, adapter)
- }
-
- @SuppressLint("SetTextI18n")
- override fun bindViewHolder(
- adapter: FlexibleAdapter<IFlexible<*>?>,
- holder: ConversationItemViewHolder,
- position: Int,
- payloads: List<Any>
- ) {
- val appContext = sharedApplication!!.applicationContext
- holder.binding.dialogName.setTextColor(
- ResourcesCompat.getColor(
- context.resources,
- R.color.conversation_item_header,
- null
- )
- )
- if (adapter.hasFilter()) {
- viewThemeUtils.platform.highlightText(
- holder.binding.dialogName,
- model.displayName!!, adapter.getFilter(String::class.java).toString()
- )
- } else {
- holder.binding.dialogName.text = model.displayName
- }
- if (model.unreadMessages > 0) {
- holder.binding.dialogName.setTypeface(holder.binding.dialogName.typeface, Typeface.BOLD)
- holder.binding.dialogLastMessage.setTypeface(holder.binding.dialogLastMessage.typeface, Typeface.BOLD)
- holder.binding.dialogUnreadBubble.visibility = View.VISIBLE
- if (model.unreadMessages < 1000) {
- holder.binding.dialogUnreadBubble.text = model.unreadMessages.toLong().toString()
- } else {
- holder.binding.dialogUnreadBubble.setText(R.string.tooManyUnreadMessages)
- }
- val lightBubbleFillColor = ColorStateList.valueOf(
- ContextCompat.getColor(
- context,
- R.color.conversation_unread_bubble
- )
- )
- val lightBubbleTextColor = ContextCompat.getColor(
- context,
- R.color.conversation_unread_bubble_text
- )
- if (model.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
- viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
- } else if (model.unreadMention) {
- if (hasSpreedFeatureCapability(user, "direct-mention-flag")) {
- if (model.unreadMentionDirect!!) {
- viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
- } else {
- viewThemeUtils.material.colorChipOutlined(holder.binding.dialogUnreadBubble, 6.0f)
- }
- } else {
- viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
- }
- } else {
- holder.binding.dialogUnreadBubble.chipBackgroundColor = lightBubbleFillColor
- holder.binding.dialogUnreadBubble.setTextColor(lightBubbleTextColor)
- }
- } else {
- holder.binding.dialogName.setTypeface(null, Typeface.NORMAL)
- holder.binding.dialogDate.setTypeface(null, Typeface.NORMAL)
- holder.binding.dialogLastMessage.setTypeface(null, Typeface.NORMAL)
- holder.binding.dialogUnreadBubble.visibility = View.GONE
- }
- if (model.favorite) {
- holder.binding.favoriteConversationImageView.visibility = View.VISIBLE
- } else {
- holder.binding.favoriteConversationImageView.visibility = View.GONE
- }
- if (status != null && ConversationType.ROOM_SYSTEM !== model.type) {
- val size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, appContext)
- holder.binding.userStatusImage.visibility = View.VISIBLE
- holder.binding.userStatusImage.setImageDrawable(
- StatusDrawable(
- status.status,
- status.icon,
- size,
- context.resources.getColor(R.color.bg_default),
- appContext
- )
- )
- } else {
- holder.binding.userStatusImage.visibility = View.GONE
- }
- if (model.lastMessage != null) {
- holder.binding.dialogDate.visibility = View.VISIBLE
- holder.binding.dialogDate.text = DateUtils.getRelativeTimeSpanString(
- model.lastActivity * 1000L,
- System.currentTimeMillis(),
- 0,
- DateUtils.FORMAT_ABBREV_RELATIVE
- )
- if (!TextUtils.isEmpty(model.lastMessage!!.systemMessage) ||
- ConversationType.ROOM_SYSTEM === model.type
- ) {
- holder.binding.dialogLastMessage.text = model.lastMessage!!.text
- } else {
- model.lastMessage!!.activeUser = user
- val text: String
- if (model.lastMessage!!.getCalculateMessageType() === ChatMessage.MessageType.REGULAR_TEXT_MESSAGE) {
- if (model.lastMessage!!.actorId == user.userId) {
- text = String.format(
- appContext.getString(R.string.nc_formatted_message_you),
- model.lastMessage!!.lastMessageDisplayText
- )
- } else {
- val authorDisplayName =
- if (!TextUtils.isEmpty(model.lastMessage!!.actorDisplayName)) {
- model.lastMessage!!.actorDisplayName
- } else if ("guests" == model.lastMessage!!.actorType) {
- appContext.getString(R.string.nc_guest)
- } else {
- ""
- }
- text = String.format(
- appContext.getString(R.string.nc_formatted_message),
- authorDisplayName,
- model.lastMessage!!.lastMessageDisplayText
- )
- }
- } else {
- text = model.lastMessage!!.lastMessageDisplayText
- }
- holder.binding.dialogLastMessage.text = text
- }
- } else {
- holder.binding.dialogDate.visibility = View.GONE
- holder.binding.dialogLastMessage.setText(R.string.nc_no_messages_yet)
- }
- holder.binding.dialogAvatar.visibility = View.VISIBLE
- var shouldLoadAvatar = true
- var objectType: String?
- if (!TextUtils.isEmpty(model.objectType.also { objectType = it })) {
- when (objectType) {
- "share:password" -> {
- shouldLoadAvatar = false
- holder.binding.dialogAvatar.setImageDrawable(
- ContextCompat.getDrawable(
- context,
- R.drawable.ic_circular_lock
- )
- )
- }
- "file" -> {
- shouldLoadAvatar = false
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- holder.binding.dialogAvatar.setImageDrawable(
- DisplayUtils.getRoundedDrawable(
- viewThemeUtils.talk.themePlaceholderAvatar(
- holder.binding.dialogAvatar,
- R.drawable.ic_avatar_document
- )
- )
- )
- } else {
- holder.binding.dialogAvatar.setImageDrawable(
- ContextCompat.getDrawable(context, R.drawable.ic_circular_document)
- )
- }
- }
- else -> {}
- }
- }
- if (ConversationType.ROOM_SYSTEM == model.type) {
- 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)
- val layerDrawable = LayerDrawable(layers)
- holder.binding.dialogAvatar.loadAvatar(DisplayUtils.getRoundedDrawable(layerDrawable))
- } else {
- 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)) {
- holder.binding.dialogAvatar.loadAvatar(user, model.name!!)
- } else {
- holder.binding.dialogAvatar.visibility = View.GONE
- }
- ConversationType.ROOM_GROUP_CALL -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- holder.binding.dialogAvatar.setImageDrawable(
- DisplayUtils.getRoundedDrawable(
- viewThemeUtils.talk.themePlaceholderAvatar(
- holder.binding.dialogAvatar,
- R.drawable.ic_avatar_group
- )
- )
- )
- } else {
- holder.binding.dialogAvatar.setImageDrawable(
- ContextCompat.getDrawable(context, R.drawable.ic_circular_group)
- )
- }
- ConversationType.ROOM_PUBLIC_CALL -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- holder.binding.dialogAvatar.setImageDrawable(
- DisplayUtils.getRoundedDrawable(
- viewThemeUtils.talk.themePlaceholderAvatar(
- holder.binding.dialogAvatar,
- R.drawable.ic_avatar_link
- )
- )
- )
- } else {
- holder.binding.dialogAvatar.setImageDrawable(
- ContextCompat.getDrawable(context, R.drawable.ic_circular_link)
- )
- }
- else -> holder.binding.dialogAvatar.visibility = View.GONE
- }
- }
- }
-
- override fun filter(constraint: String?): Boolean {
- return model.displayName != null &&
- Pattern
- .compile(constraint!!, Pattern.CASE_INSENSITIVE or Pattern.LITERAL)
- .matcher(model.displayName!!.trim { it <= ' ' })
- .find()
- }
-
- override fun getHeader(): GenericTextHeaderItem? {
- return header
- }
-
- override fun setHeader(header: GenericTextHeaderItem?) {
- this.header = header
- }
-
- class ConversationItemViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter) {
- var binding: RvItemConversationWithLastMessageBinding
-
- init {
- binding = RvItemConversationWithLastMessageBinding.bind(view!!)
- }
- }
-
- companion object {
- const val VIEW_TYPE = R.layout.rv_item_conversation_with_last_message
- private const val STATUS_SIZE_IN_DP = 9f
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt
index 76b17cba9..d00df59d4 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt
@@ -2,8 +2,6 @@
* 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
*
@@ -29,9 +27,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
@@ -74,7 +72,7 @@ data class MessageResultItem constructor(
) {
holder.binding.conversationTitle.text = messageEntry.title
bindMessageExcerpt(holder)
- holder.binding.thumbnail.loadThumbnail(messageEntry.thumbnailURL, currentUser)
+ loadImage(holder)
}
private fun bindMessageExcerpt(holder: ViewHolder) {
@@ -85,6 +83,17 @@ 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 {
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt
index fd666ed36..14abbc2f2 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt
@@ -587,16 +587,16 @@ class ConversationsListController(bundle: Bundle) :
if (activity != null) {
val conversationItem = ConversationItem(
conversation,
- currentUser!!,
- activity!!,
+ currentUser,
+ activity,
userStatuses[conversation.name],
viewThemeUtils
)
conversationItems.add(conversationItem)
val conversationItemWithHeader = ConversationItem(
conversation,
- currentUser!!,
- activity!!,
+ currentUser,
+ activity,
callHeaderItems[headerTitle],
userStatuses[conversation.name],
viewThemeUtils
@@ -659,8 +659,8 @@ class ConversationsListController(bundle: Bundle) :
}
val conversationItem = ConversationItem(
conversation,
- currentUser!!,
- activity!!,
+ currentUser,
+ activity,
callHeaderItems[headerTitle],
userStatuses[conversation.name],
viewThemeUtils
diff --git a/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt b/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt
deleted file mode 100644
index d23ba8f37..000000000
--- a/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java
index 7668215c6..3f3d2ed9b 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java
+++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java
@@ -32,11 +32,6 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.BitmapDrawable;
@@ -169,28 +164,21 @@ public class DisplayUtils {
}
}
- public static Bitmap roundBitmap(Bitmap bitmap) {
- Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
-
- final Canvas canvas = new Canvas(output);
-
- final Paint paint = new Paint();
- final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
- final RectF rectF = new RectF(rect);
-
- paint.setAntiAlias(true);
- canvas.drawARGB(0, 0, 0, 0);
- canvas.drawOval(rectF, paint);
-
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(bitmap, rect, rect, paint);
+ public static Drawable getRoundedDrawable(Drawable drawable) {
+ Bitmap bitmap = getBitmap(drawable);
+ new RoundAsCirclePostprocessor(true).process(bitmap);
+ return new BitmapDrawable(bitmap);
+ }
- return output;
+ public static Bitmap getRoundedBitmapFromVectorDrawableResource(Resources resources, int resource) {
+ VectorDrawable vectorDrawable = (VectorDrawable) ResourcesCompat.getDrawable(resources, resource, null);
+ Bitmap bitmap = getBitmap(vectorDrawable);
+ new RoundPostprocessor(true).process(bitmap);
+ return bitmap;
}
- public static Drawable getRoundedDrawable(Drawable drawable) {
- Bitmap bitmap = getBitmap(drawable);
- return new BitmapDrawable(roundBitmap(bitmap));
+ public static Drawable getRoundedBitmapDrawableFromVectorDrawableResource(Resources resources, int resource) {
+ return new BitmapDrawable(getRoundedBitmapFromVectorDrawableResource(resources, resource));
}
public static Bitmap getBitmap(Drawable drawable) {
@@ -617,6 +605,30 @@ 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()) {
diff --git a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
index c361b6503..4341f18c1 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
@@ -38,6 +38,7 @@ import com.facebook.common.references.CloseableReference
import com.facebook.datasource.DataSources
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.image.CloseableBitmap
+import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor
import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User
@@ -333,7 +334,10 @@ object NotificationUtils {
val closeableImageRef = DataSources.waitForFinalResult(dataSource) as CloseableReference<CloseableBitmap>?
val bitmap = closeableImageRef?.get()?.underlyingBitmap
if (bitmap != null) {
- avatarIcon = IconCompat.createWithBitmap(DisplayUtils.roundBitmap(bitmap))
+ // According to Fresco documentation a copy of the bitmap should be made before closing the references.
+ // However, it seems to work without making a copy... ;-)
+ RoundAsCirclePostprocessor(true).process(bitmap)
+ avatarIcon = IconCompat.createWithBitmap(bitmap)
}
CloseableReference.closeSafely(closeableImageRef)
dataSource.close()
diff --git a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml
index c13b8fe72..0428af241 100644
--- a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml
+++ b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml
@@ -38,7 +38,7 @@
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/double_margin_between_elements">
- <ImageView
+ <com.facebook.drawee.view.SimpleDraweeView
android:id="@id/dialogAvatar"
android:layout_width="@dimen/small_item_height"
android:layout_height="@dimen/small_item_height"
diff --git a/app/src/main/res/layout/rv_item_search_message.xml b/app/src/main/res/layout/rv_item_search_message.xml
index c531d4e39..a8ec4aaaf 100644
--- a/app/src/main/res/layout/rv_item_search_message.xml
+++ b/app/src/main/res/layout/rv_item_search_message.xml
@@ -3,8 +3,6 @@
~
~ @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>
~
@@ -37,7 +35,7 @@
android:layout_margin="@dimen/double_margin_between_elements"
tools:background="@color/white">
- <ImageView
+ <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/thumbnail"
android:layout_width="@dimen/small_item_height"
android:layout_height="@dimen/small_item_height"
diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt
index fa59ff276..7296f257e 100644
--- a/scripts/analysis/findbugs-results.txt
+++ b/scripts/analysis/findbugs-results.txt
@@ -1 +1 @@
-134 \ No newline at end of file
+136