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
path: root/app
diff options
context:
space:
mode:
authordrone <drone@localhost>2022-09-29 16:16:56 +0300
committerdrone <drone@localhost>2022-09-29 16:16:56 +0300
commit3a90d176495b7b6c7e965cf87d6e2b604831c1c0 (patch)
tree4bcd53009e456b6bb49c3f7d67176b7907d6f636 /app
parent1382a23b39a17217f1782a30cae1ed7f11157737 (diff)
parent863052b53e3051bb7e0fd4904d348fa294835821 (diff)
Merge commit '863052b53e3051bb7e0fd4904d348fa294835821'
Diffstat (limited to 'app')
-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
10 files changed, 490 insertions, 434 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 5cb2e675a..22dd9ab25 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -42,7 +42,7 @@ android {
namespace 'com.nextcloud.talk'
defaultConfig {
- minSdkVersion 21
+ minSdkVersion 23
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
deleted file mode 100644
index 7110277df..000000000
--- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * 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
new file mode 100644
index 000000000..576ddccae
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt
@@ -0,0 +1,352 @@
+/*
+ * 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 d00df59d4..76b17cba9 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,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 {
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 14abbc2f2..fd666ed36 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
new file mode 100644
index 000000000..d23ba8f37
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt
@@ -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
+ }
+}
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 3f3d2ed9b..7668215c6 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java
+++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java
@@ -32,6 +32,11 @@ 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;
@@ -164,21 +169,28 @@ public class DisplayUtils {
}
}
- public static Drawable getRoundedDrawable(Drawable drawable) {
- Bitmap bitmap = getBitmap(drawable);
- new RoundAsCirclePostprocessor(true).process(bitmap);
- return new BitmapDrawable(bitmap);
- }
+ public static Bitmap roundBitmap(Bitmap bitmap) {
+ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
- 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;
+ 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);
+
+ return output;
}
- public static Drawable getRoundedBitmapDrawableFromVectorDrawableResource(Resources resources, int resource) {
- return new BitmapDrawable(getRoundedBitmapFromVectorDrawableResource(resources, resource));
+ public static Drawable getRoundedDrawable(Drawable drawable) {
+ Bitmap bitmap = getBitmap(drawable);
+ return new BitmapDrawable(roundBitmap(bitmap));
}
public static Bitmap getBitmap(Drawable drawable) {
@@ -605,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()) {
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 4341f18c1..c361b6503 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
@@ -38,7 +38,6 @@ 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
@@ -334,10 +333,7 @@ object NotificationUtils {
val closeableImageRef = DataSources.waitForFinalResult(dataSource) as CloseableReference<CloseableBitmap>?
val bitmap = closeableImageRef?.get()?.underlyingBitmap
if (bitmap != null) {
- // 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)
+ avatarIcon = IconCompat.createWithBitmap(DisplayUtils.roundBitmap(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 0428af241..c13b8fe72 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">
- <com.facebook.drawee.view.SimpleDraweeView
+ <ImageView
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 a8ec4aaaf..c531d4e39 100644
--- a/app/src/main/res/layout/rv_item_search_message.xml
+++ b/app/src/main/res/layout/rv_item_search_message.xml
@@ -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"