From 9accac33256becb442414c079a39bde44f622606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Thu, 4 Nov 2021 15:27:43 +0100 Subject: [PATCH] Show profile action on avatar click MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For every click on a avatar in a group chat it will be tried to receive a hover card from the Nextcloud server. The endpoint returns multiple actions. Those actions will be filtered for the app ids 'spreed', 'email' and 'profile'. Other will be ignored. The received filtered actions will be shown in a bottom sheet. Resolves: #1644 Signed-off-by: Tim Krüger --- .../IncomingLocationMessageViewHolder.kt | 10 +- .../IncomingPreviewMessageViewHolder.java | 6 +- .../IncomingVoiceMessageViewHolder.kt | 10 +- .../MagicIncomingTextMessageViewHolder.kt | 10 +- .../MagicPreviewMessageViewHolder.java | 12 +- .../OutcomingPreviewMessageViewHolder.java | 4 +- .../java/com/nextcloud/talk/api/NcApi.java | 4 + .../talk/controllers/ChatController.kt | 18 +- .../talk/models/json/hovercard/HoverCard.java | 96 ++++++++ .../json/hovercard/HoverCardAction.java | 107 +++++++++ .../models/json/hovercard/HoverCardOCS.java | 69 ++++++ .../json/hovercard/HoverCardOverall.java | 64 ++++++ .../ui/bottom/sheet/ProfileBottomSheet.kt | 217 ++++++++++++++++++ .../com/nextcloud/talk/utils/ApiUtils.java | 3 + app/src/main/res/drawable/ic_talk.xml | 12 + 15 files changed, 627 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCard.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardAction.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOCS.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOverall.java create mode 100644 app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt create mode 100644 app/src/main/res/drawable/ic_talk.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt index 8be30ca94..b55017140 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt @@ -4,6 +4,8 @@ * @author Mario Danic * @author Marcel Hibbe * @author Andy Scherzinger + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2021 Marcel Hibbe * Copyright (C) 2017-2018 Mario Danic @@ -48,6 +50,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.databinding.ItemCustomIncomingLocationMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.preferences.AppPreferences @@ -56,8 +59,8 @@ import java.net.URLEncoder import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders -.IncomingTextMessageViewHolder(incomingView) { +class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : MessageHolders +.IncomingTextMessageViewHolder(incomingView, payload) { private val binding: ItemCustomIncomingLocationMessageBinding = ItemCustomIncomingLocationMessageBinding.bind(itemView) @@ -102,6 +105,9 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders val author: String = message.actorDisplayName if (!TextUtils.isEmpty(author)) { binding.messageAuthor.text = author + binding.messageUserAvatar.setOnClickListener { + (payload as? ProfileBottomSheet)?.showFor(message.actorId, itemView.context) + } } else { binding.messageAuthor.setText(R.string.nc_nick_guest) } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java index 1a974ac67..e1d06575e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPreviewMessageViewHolder.java @@ -2,6 +2,8 @@ * Nextcloud Talk application * * @author Andy Scherzinger + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify @@ -31,8 +33,8 @@ import androidx.emoji.widget.EmojiTextView; public class IncomingPreviewMessageViewHolder extends MagicPreviewMessageViewHolder { private final ItemCustomIncomingPreviewMessageBinding binding; - public IncomingPreviewMessageViewHolder(View itemView) { - super(itemView); + public IncomingPreviewMessageViewHolder(View itemView, Object payload) { + super(itemView, payload); binding = ItemCustomIncomingPreviewMessageBinding.bind(itemView); } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt index 870a0951f..f03b19839 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt @@ -4,6 +4,8 @@ * @author Mario Danic * @author Marcel Hibbe * @author Andy Scherzinger + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2021 Marcel Hibbe * Copyright (C) 2017-2018 Mario Danic @@ -46,6 +48,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.databinding.ItemCustomIncomingVoiceMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.preferences.AppPreferences @@ -54,8 +57,8 @@ import java.util.concurrent.ExecutionException import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class IncomingVoiceMessageViewHolder(incomingView: View) : MessageHolders -.IncomingTextMessageViewHolder(incomingView) { +class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : MessageHolders +.IncomingTextMessageViewHolder(incomingView, payload) { private val binding: ItemCustomIncomingVoiceMessageBinding = ItemCustomIncomingVoiceMessageBinding.bind(itemView) @@ -192,6 +195,9 @@ class IncomingVoiceMessageViewHolder(incomingView: View) : MessageHolders val author: String = message.actorDisplayName if (!TextUtils.isEmpty(author)) { binding.messageAuthor.text = author + binding.messageUserAvatar.setOnClickListener { + (payload as? ProfileBottomSheet)?.showFor(message.actorId, itemView.context) + } } else { binding.messageAuthor.setText(R.string.nc_nick_guest) } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt index 80023f38c..37c713449 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt @@ -3,6 +3,8 @@ * * @author Mario Danic * @author Andy Scherzinger + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2017-2018 Mario Danic * @@ -44,6 +46,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.databinding.ItemCustomIncomingTextMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils @@ -53,8 +56,8 @@ import com.stfalcon.chatkit.messages.MessageHolders import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders -.IncomingTextMessageViewHolder(itemView) { +class MagicIncomingTextMessageViewHolder(itemView: View, payload: Any) : MessageHolders +.IncomingTextMessageViewHolder(itemView, payload) { private val binding: ItemCustomIncomingTextMessageBinding = ItemCustomIncomingTextMessageBinding.bind(itemView) @@ -72,6 +75,9 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders val author: String = message.actorDisplayName if (!TextUtils.isEmpty(author)) { binding.messageAuthor.text = author + binding.messageUserAvatar.setOnClickListener { + (payload as? ProfileBottomSheet)?.showFor(message.actorId, itemView.context) + } } else { binding.messageAuthor.setText(R.string.nc_nick_guest) } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java index 5cc616ad3..703a59e84 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java @@ -4,6 +4,8 @@ * @author Mario Danic * @author Marcel Hibbe * @author Andy Scherzinger + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger * Copyright (C) 2021 Marcel Hibbe * Copyright (C) 2017-2018 Mario Danic @@ -54,6 +56,7 @@ import com.nextcloud.talk.jobs.DownloadFileToCacheWorker; import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.chat.ChatMessage; +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet; import com.nextcloud.talk.utils.AccountUtils; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DrawableUtils; @@ -110,8 +113,8 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom View clickView; - public MagicPreviewMessageViewHolder(View itemView) { - super(itemView); + public MagicPreviewMessageViewHolder(View itemView, Object payload) { + super(itemView, payload); NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); } @@ -128,6 +131,11 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom } } else { userAvatar.setVisibility(View.VISIBLE); + userAvatar.setOnClickListener(v -> { + if (payload instanceof ProfileBottomSheet){ + ((ProfileBottomSheet) payload).showFor(message.actorId, v.getContext()); + } + }); if (ACTOR_TYPE_BOTS.equals(message.actorType) && ACTOR_ID_CHANGELOG.equals(message.actorId)) { if (context != null) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java index b91aa5ba5..14ba39cee 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPreviewMessageViewHolder.java @@ -2,6 +2,8 @@ * Nextcloud Talk application * * @author Andy Scherzinger + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger * Copyright (C) 2021 Andy Scherzinger * * This program is free software: you can redistribute it and/or modify @@ -32,7 +34,7 @@ public class OutcomingPreviewMessageViewHolder extends MagicPreviewMessageViewHo private final ItemCustomOutcomingPreviewMessageBinding binding; public OutcomingPreviewMessageViewHolder(View itemView) { - super(itemView); + super(itemView, null); binding = ItemCustomOutcomingPreviewMessageBinding.bind(itemView); } diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 334c95f71..79b25739b 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -31,6 +31,7 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall; import com.nextcloud.talk.models.json.conversations.RoomsOverall; import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.models.json.generic.Status; +import com.nextcloud.talk.models.json.hovercard.HoverCardOverall; import com.nextcloud.talk.models.json.mention.MentionOverall; import com.nextcloud.talk.models.json.notifications.NotificationOverall; import com.nextcloud.talk.models.json.participants.AddParticipantOverall; @@ -425,4 +426,7 @@ public interface NcApi { @POST Observable notificationCalls(@Header("Authorization") String authorization, @Url String url, @Field("level") Integer level); + + @GET + Observable hoverCard(@Header("Authorization") String authorization, @Url String url); } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index fa3e50144..cd18dc807 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -132,6 +132,7 @@ import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.mention.Mention import com.nextcloud.talk.presenters.MentionAutocompletePresenter +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.ui.dialog.AttachmentDialog import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback @@ -427,9 +428,12 @@ class ChatController(args: Bundle) : adapterWasNull = true val messageHolders = MessageHolders() + val profileBottomSheet = ProfileBottomSheet(ncApi!!, conversationUser!!, router) + messageHolders.setIncomingTextConfig( MagicIncomingTextMessageViewHolder::class.java, - R.layout.item_custom_incoming_text_message + R.layout.item_custom_incoming_text_message, + profileBottomSheet ) messageHolders.setOutcomingTextConfig( MagicOutcomingTextMessageViewHolder::class.java, @@ -438,7 +442,8 @@ class ChatController(args: Bundle) : messageHolders.setIncomingImageConfig( IncomingPreviewMessageViewHolder::class.java, - R.layout.item_custom_incoming_preview_message + R.layout.item_custom_incoming_preview_message, + profileBottomSheet ) messageHolders.setOutcomingImageConfig( @@ -460,14 +465,17 @@ class ChatController(args: Bundle) : MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, MagicUnreadNoticeMessageViewHolder::class.java, - R.layout.item_date_header, this + R.layout.item_date_header, + this ) messageHolders.registerContentType( CONTENT_TYPE_LOCATION, IncomingLocationMessageViewHolder::class.java, + profileBottomSheet, R.layout.item_custom_incoming_location_message, OutcomingLocationMessageViewHolder::class.java, + null, R.layout.item_custom_outcoming_location_message, this ) @@ -475,8 +483,10 @@ class ChatController(args: Bundle) : messageHolders.registerContentType( CONTENT_TYPE_VOICE_MESSAGE, IncomingVoiceMessageViewHolder::class.java, + profileBottomSheet, R.layout.item_custom_incoming_voice_message, OutcomingVoiceMessageViewHolder::class.java, + null, R.layout.item_custom_outcoming_voice_message, this ) @@ -2178,7 +2188,7 @@ class ChatController(args: Bundle) : bundle.putBoolean(BundleKeys.KEY_FORWARD_MSG_FLAG, true) bundle.putString(BundleKeys.KEY_FORWARD_MSG_TEXT, message?.text) bundle.putString(BundleKeys.KEY_FORWARD_HIDE_SOURCE_ROOM, roomId) - getRouter().pushController( + router.pushController( RouterTransaction.with(ConversationsListController(bundle)) .pushChangeHandler(HorizontalChangeHandler()) .popChangeHandler(HorizontalChangeHandler()) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCard.java b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCard.java new file mode 100644 index 000000000..9a98cd870 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCard.java @@ -0,0 +1,96 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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 . + */ +package com.nextcloud.talk.models.json.hovercard; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.List; +import java.util.Objects; + +@Parcel +@JsonObject +public class HoverCard { + + @JsonField(name = "userId") + public String userId; + + @JsonField(name = "displayName") + public String displayName; + + @JsonField(name = "actions") + public List actions; + + + public String getUserId() { + return this.userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public List getActions() { + return actions; + } + + public void setActions(List actions) { + this.actions = actions; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HoverCard hoverCard = (HoverCard) o; + return Objects.equals(userId, hoverCard.userId) && + Objects.equals(displayName, hoverCard.displayName) && + Objects.equals(actions, hoverCard.actions); + } + + @Override + public int hashCode() { + return Objects.hash(userId, displayName, actions); + } + + @Override + public String toString() { + return "HoverCard{" + + "userId='" + userId + '\'' + + ", displayName='" + displayName + '\'' + + ", actions=" + actions + + '}'; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardAction.java b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardAction.java new file mode 100644 index 000000000..4153ef8e1 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardAction.java @@ -0,0 +1,107 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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 . + */ +package com.nextcloud.talk.models.json.hovercard; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.Objects; + +@Parcel +@JsonObject +public class HoverCardAction { + + @JsonField(name = "title") + public String title; + + @JsonField(name = "icon") + public String icon; + + @JsonField(name = "hyperlink") + public String hyperlink; + + @JsonField(name = "appId") + public String appId; + + public String getTitle() { + return this.title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getHyperlink() { + return hyperlink; + } + + public void setHyperlink(String hyperlink) { + this.hyperlink = hyperlink; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HoverCardAction that = (HoverCardAction) o; + return Objects.equals(title, that.title) && + Objects.equals(icon, that.icon) && + Objects.equals(hyperlink, that.hyperlink) && + Objects.equals(appId, that.appId); + } + + @Override + public int hashCode() { + return Objects.hash(title, icon, hyperlink, appId); + } + + @Override + public String toString() { + return "HoverCardAction{" + + "title='" + title + '\'' + + ", icon='" + icon + '\'' + + ", hyper='" + hyperlink + '\'' + + ", appId='" + appId + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOCS.java new file mode 100644 index 000000000..cafc659a4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOCS.java @@ -0,0 +1,69 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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 . + */ +package com.nextcloud.talk.models.json.hovercard; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.generic.GenericOCS; + +import java.util.Objects; + +@JsonObject +public class HoverCardOCS extends GenericOCS { + @JsonField(name = "data") + public HoverCard data; + + public HoverCard getData() { + return this.data; + } + + public void setData(HoverCard data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + HoverCardOCS that = (HoverCardOCS) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), data); + } + + @Override + public String toString() { + return "HoverCardOCS{" + + "data=" + data + + '}'; + } + +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOverall.java new file mode 100644 index 000000000..c73bebe94 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/hovercard/HoverCardOverall.java @@ -0,0 +1,64 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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 . + */ +package com.nextcloud.talk.models.json.hovercard; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import java.util.Objects; + +@JsonObject +public class HoverCardOverall { + @JsonField(name = "ocs") + public HoverCardOCS ocs; + + public HoverCardOCS getOcs() { + return this.ocs; + } + + public void setOcs(HoverCardOCS ocs) { + this.ocs = ocs; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HoverCardOverall that = (HoverCardOverall) o; + return Objects.equals(ocs, that.ocs); + } + + @Override + public int hashCode() { + return Objects.hash(ocs); + } + + @Override + public String toString() { + return "HoverCardOverall{" + + "ocs=" + ocs + + '}'; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt new file mode 100644 index 000000000..d9ed5bb59 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt @@ -0,0 +1,217 @@ +/* + * + * Nextcloud Talk application + * + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * + * 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 . + */ +package com.nextcloud.talk.ui.bottom.sheet + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.util.Log +import com.afollestad.materialdialogs.LayoutMode +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.bottomsheets.BottomSheet +import com.bluelinelabs.conductor.Router +import com.nextcloud.talk.R +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage +import com.nextcloud.talk.controllers.bottomsheet.items.listItemsWithImage +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.hovercard.HoverCardAction +import com.nextcloud.talk.models.json.hovercard.HoverCardOverall +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet.AllowedAppIds.EMAIL +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet.AllowedAppIds.PROFILE +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet.AllowedAppIds.SPREED +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.ConductorRemapping +import com.nextcloud.talk.utils.bundle.BundleKeys +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import org.parceler.Parcels + +private const val TAG = "ProfileBottomSheet" + +class ProfileBottomSheet(val ncApi: NcApi, val userEntity: UserEntity, val router: Router) { + + private val allowedAppIds = listOf(SPREED.stringValue, PROFILE.stringValue, EMAIL.stringValue) + + fun showFor(user: String, context: Context) { + + ncApi.hoverCard( + ApiUtils.getCredentials(userEntity.username, userEntity.token), + ApiUtils.getUrlForHoverCard(userEntity.baseUrl, user) + ).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : io.reactivex.Observer { + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(hoverCardOverall: HoverCardOverall) { + bottomSheet(hoverCardOverall.ocs.data.actions, hoverCardOverall.ocs.data.displayName, user, context) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to get hover card for user $user", e) + } + + override fun onComplete() { + } + }) + } + + private fun bottomSheet(actions: List, displayName: String, userId: String, context: Context) { + + val filteredActions = actions.filter { allowedAppIds.contains(it.appId) } + val items = filteredActions.map { configureActionListItem(it) } + + MaterialDialog(context, BottomSheet(LayoutMode.WRAP_CONTENT)).show { + cornerRadius(res = R.dimen.corner_radius) + + title(text = displayName) + listItemsWithImage(items = items) { _, index, _ -> + + val action = filteredActions[index] + + when (AllowedAppIds.createFor(action)) { + PROFILE -> openProfile(action.hyperlink, context) + EMAIL -> composeEmail(action.title, context) + SPREED -> talkTo(userId) + } + } + } + } + + private fun configureActionListItem(action: HoverCardAction): BasicListItemWithImage { + + val drawable = when (AllowedAppIds.createFor(action)) { + PROFILE -> R.drawable.ic_user + EMAIL -> R.drawable.ic_email + SPREED -> R.drawable.ic_talk + } + + return BasicListItemWithImage( + drawable, + action.title + ) + } + + private fun talkTo(userId: String) { + + val apiVersion = + ApiUtils.getConversationApiVersion(userEntity, intArrayOf(ApiUtils.APIv4, 1)) + val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + userEntity.baseUrl, + "1", + null, + userId, + null + ) + val credentials = ApiUtils.getCredentials(userEntity.username, userEntity.token) + ncApi!!.createRoom( + credentials, + retrofitBucket.getUrl(), retrofitBucket.getQueryMap() + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(roomOverall: RoomOverall) { + val bundle = Bundle() + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, userEntity) + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken()) + bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.getOcs().getData().getRoomId()) + + // FIXME once APIv2+ is used only, the createRoom already returns all the data + ncApi!!.getRoom( + credentials, + ApiUtils.getUrlForRoom( + apiVersion, userEntity.baseUrl, + roomOverall.getOcs().getData().getToken() + ) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(roomOverall: RoomOverall) { + bundle.putParcelable( + BundleKeys.KEY_ACTIVE_CONVERSATION, + Parcels.wrap(roomOverall.getOcs().getData()) + ) + ConductorRemapping.remapChatController( + router, userEntity.id, + roomOverall.getOcs().getData().getToken(), bundle, true + ) + } + + override fun onError(e: Throwable) { + Log.e(TAG, e.message, e) + } + + override fun onComplete() { + // unused atm + } + }) + } + + override fun onError(e: Throwable) { + Log.e(TAG, e.message, e) + } + + override fun onComplete() { + // unused atm + } + }) + } + + private fun composeEmail(address: String, context: Context) { + val addresses = arrayListOf(address) + val intent = Intent(Intent.ACTION_SENDTO).apply { + data = Uri.parse("mailto:") // only email apps should handle this + putExtra(Intent.EXTRA_EMAIL, addresses) + } + context.startActivity(intent) + } + + private fun openProfile(hyperlink: String, context: Context) { + val webpage: Uri = Uri.parse(hyperlink) + val intent = Intent(Intent.ACTION_VIEW, webpage) + context.startActivity(intent) + } + + enum class AllowedAppIds(val stringValue: String) { + SPREED("spreed"), + PROFILE("profile"), + EMAIL("email"); + + companion object { + fun createFor(action: HoverCardAction): AllowedAppIds = valueOf(action.appId.uppercase()) + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 2a0687a14..7bc3ee1ea 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -400,4 +400,7 @@ public class ApiUtils { public static String getUrlToSendLocation(int version, String baseUrl, String roomToken) { return getUrlForChat(version, baseUrl, roomToken) + "/share"; } + + public static String getUrlForHoverCard(String baseUrl, String userId) { return baseUrl + ocsApiVersion + + "/hovercard/v1/" + userId; } } diff --git a/app/src/main/res/drawable/ic_talk.xml b/app/src/main/res/drawable/ic_talk.xml new file mode 100644 index 000000000..c479856dc --- /dev/null +++ b/app/src/main/res/drawable/ic_talk.xml @@ -0,0 +1,12 @@ + + + +