From f2025332ab83d4c58b953467011b7afc2018b8af Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 24 May 2022 15:31:16 +0200 Subject: [PATCH 01/92] add data classes for polls (WIP) Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 241 ++++++++++++++++++ .../OutcomingPollMessageViewHolder.kt | 175 +++++++++++++ .../java/com/nextcloud/talk/api/NcApi.java | 19 +- .../talk/controllers/ChatController.kt | 15 ++ .../talk/models/json/chat/ChatMessage.kt | 32 ++- .../talk/polls/repositories/model/Poll.kt | 65 +++++ .../talk/polls/repositories/model/PollOCS.kt | 35 +++ .../polls/repositories/model/PollOverall.kt | 35 +++ .../com/nextcloud/talk/utils/ApiUtils.java | 52 ++-- .../res/drawable/ic_baseline_bar_chart_24.xml | 10 + .../item_custom_incoming_poll_message.xml | 109 ++++++++ .../item_custom_outcoming_poll_message.xml | 78 ++++++ app/src/main/res/values/strings.xml | 7 + 13 files changed, 854 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOverall.kt create mode 100644 app/src/main/res/drawable/ic_baseline_bar_chart_24.xml create mode 100644 app/src/main/res/layout/item_custom_incoming_poll_message.xml create mode 100644 app/src/main/res/layout/item_custom_outcoming_poll_message.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt new file mode 100644 index 000000000..8ada42208 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -0,0 +1,241 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2021 Marcel Hibbe + * + * 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.adapters.messages + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.os.Build +import android.text.TextUtils +import android.view.View +import androidx.core.content.ContextCompat +import androidx.core.content.res.ResourcesCompat +import androidx.core.view.ViewCompat +import autodagger.AutoInjector +import coil.load +import com.amulyakhare.textdrawable.TextDrawable +import com.nextcloud.talk.R +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.databinding.ItemCustomIncomingPollMessageBinding +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 +import com.stfalcon.chatkit.messages.MessageHolders +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageHolders +.IncomingTextMessageViewHolder(incomingView, payload) { + + private val binding: ItemCustomIncomingPollMessageBinding = + ItemCustomIncomingPollMessageBinding.bind(itemView) + + @JvmField + @Inject + var context: Context? = null + + @JvmField + @Inject + var appPreferences: AppPreferences? = null + + @Inject + @JvmField + var ncApi: NcApi? = null + + lateinit var message: ChatMessage + + lateinit var reactionsInterface: ReactionsInterface + + @SuppressLint("SetTextI18n") + override fun onBind(message: ChatMessage) { + super.onBind(message) + this.message = message + sharedApplication!!.componentApplication.inject(this) + + setAvatarAndAuthorOnMessageItem(message) + + colorizeMessageBubble(message) + + itemView.isSelected = false + binding.messageTime.setTextColor(ResourcesCompat.getColor(context?.resources!!, R.color.warm_grey_four, null)) + + // parent message handling + setParentMessageDataOnMessageItem(message) + + setPollPreview(message) + + Reaction().showReactions(message, binding.reactions, binding.messageTime.context, false) + binding.reactions.reactionsEmojiWrapper.setOnClickListener { + reactionsInterface.onClickReactions(message) + } + binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> + reactionsInterface.onLongClickReactions(message) + true + } + } + + private fun setPollPreview(message: ChatMessage) { + var pollId: String? + var pollName: String? = "" + + if (message.messageParameters != null && message.messageParameters!!.size > 0) { + for (key in message.messageParameters!!.keys) { + val individualHashMap: Map = message.messageParameters!![key]!! + if (individualHashMap["type"] == "talk-poll") { + pollId = individualHashMap["id"] + pollName = individualHashMap["name"] + } + } + } + + binding.messagePollTitle.text = pollName + + // TODO: how to get room token here? + // val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) + // ncApi!!.getPoll( + // credentials, + // ApiUtils.getUrlForPoll( + // message.activeUser?.baseUrl, + // ??????? + // ) + // ) + } + + private fun setAvatarAndAuthorOnMessageItem(message: ChatMessage) { + 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) + } + + if (!message.isGrouped && !message.isOneToOneConversation) { + setAvatarOnMessage(message) + } else { + if (message.isOneToOneConversation) { + binding.messageUserAvatar.visibility = View.GONE + } else { + binding.messageUserAvatar.visibility = View.INVISIBLE + } + binding.messageAuthor.visibility = View.GONE + } + } + + private fun setAvatarOnMessage(message: ChatMessage) { + binding.messageUserAvatar.visibility = View.VISIBLE + if (message.actorType == "guests") { + // do nothing, avatar is set + } else if (message.actorType == "bots" && message.actorId == "changelog") { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val layers = arrayOfNulls(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) + binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable)) + } else { + binding.messageUserAvatar.setImageResource(R.mipmap.ic_launcher) + } + } else if (message.actorType == "bots") { + val drawable = TextDrawable.builder() + .beginConfig() + .bold() + .endConfig() + .buildRound( + ">", + ResourcesCompat.getColor(context!!.resources, R.color.black, null) + ) + binding.messageUserAvatar.visibility = View.VISIBLE + binding.messageUserAvatar.setImageDrawable(drawable) + } + } + + private fun colorizeMessageBubble(message: ChatMessage) { + val resources = itemView.resources + + var bubbleResource = R.drawable.shape_incoming_message + + if (message.isGrouped) { + bubbleResource = R.drawable.shape_grouped_incoming_message + } + + val bgBubbleColor = if (message.isDeleted) { + ResourcesCompat.getColor(resources, R.color.bg_message_list_incoming_bubble_deleted, null) + } else { + ResourcesCompat.getColor(resources, R.color.bg_message_list_incoming_bubble, null) + } + val bubbleDrawable = DisplayUtils.getMessageSelector( + bgBubbleColor, + ResourcesCompat.getColor(resources, R.color.transparent, null), + bgBubbleColor, bubbleResource + ) + ViewCompat.setBackground(bubble, bubbleDrawable) + } + + private fun setParentMessageDataOnMessageItem(message: ChatMessage) { + if (!message.isDeleted && message.parentMessage != null) { + val parentChatMessage = message.parentMessage + parentChatMessage!!.activeUser = message.activeUser + parentChatMessage!!.imageUrl?.let { + binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE + binding.messageQuote.quotedMessageImage.load(it) { + addHeader( + "Authorization", + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ) + } + } ?: run { + binding.messageQuote.quotedMessageImage.visibility = View.GONE + } + binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName + ?: context!!.getText(R.string.nc_nick_guest) + binding.messageQuote.quotedMessage.text = parentChatMessage.text + + binding.messageQuote.quotedMessageAuthor + .setTextColor(ContextCompat.getColor(context!!, R.color.textColorMaxContrast)) + + if (parentChatMessage.actorId?.equals(message.activeUser!!.userId) == true) { + binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.colorPrimary) + } else { + binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.textColorMaxContrast) + } + + binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE + } else { + binding.messageQuote.quotedChatMessageView.visibility = View.GONE + } + } + + fun assignReactionInterface(reactionsInterface: ReactionsInterface) { + this.reactionsInterface = reactionsInterface + } + + companion object { + private val TAG = NextcloudTalkApplication::class.java.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt new file mode 100644 index 000000000..fdbb4f2a4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -0,0 +1,175 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2017-2018 Mario Danic + * Copyright (C) 2021 Marcel Hibbe + * + * 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.adapters.messages + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.PorterDuff +import android.os.Handler +import android.view.View +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.view.ViewCompat +import autodagger.AutoInjector +import coil.load +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.databinding.ItemCustomOutcomingPollMessageBinding +import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.models.json.chat.ReadStatus +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.preferences.AppPreferences +import com.stfalcon.chatkit.messages.MessageHolders +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class OutcomingPollMessageViewHolder(outcomingView: View) : MessageHolders +.OutcomingTextMessageViewHolder(outcomingView) { + + private val binding: ItemCustomOutcomingPollMessageBinding = + ItemCustomOutcomingPollMessageBinding.bind(itemView) + + @JvmField + @Inject + var context: Context? = null + + @JvmField + @Inject + var appPreferences: AppPreferences? = null + + lateinit var message: ChatMessage + + lateinit var handler: Handler + + lateinit var reactionsInterface: ReactionsInterface + + @SuppressLint("SetTextI18n") + override fun onBind(message: ChatMessage) { + super.onBind(message) + this.message = message + sharedApplication!!.componentApplication.inject(this) + + colorizeMessageBubble(message) + + itemView.isSelected = false + binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60)) + + // parent message handling + setParentMessageDataOnMessageItem(message) + + val readStatusDrawableInt = when (message.readStatus) { + ReadStatus.READ -> R.drawable.ic_check_all + ReadStatus.SENT -> R.drawable.ic_check + else -> null + } + + val readStatusContentDescriptionString = when (message.readStatus) { + ReadStatus.READ -> context?.resources?.getString(R.string.nc_message_read) + ReadStatus.SENT -> context?.resources?.getString(R.string.nc_message_sent) + else -> null + } + + readStatusDrawableInt?.let { drawableInt -> + AppCompatResources.getDrawable(context!!, drawableInt)?.let { + it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP) + binding.checkMark.setImageDrawable(it) + } + } + + binding.checkMark.setContentDescription(readStatusContentDescriptionString) + + Reaction().showReactions(message, binding.reactions, binding.messageTime.context, true) + binding.reactions.reactionsEmojiWrapper.setOnClickListener { + reactionsInterface.onClickReactions(message) + } + binding.reactions.reactionsEmojiWrapper.setOnLongClickListener { l: View? -> + reactionsInterface.onLongClickReactions(message) + true + } + } + + private fun setParentMessageDataOnMessageItem(message: ChatMessage) { + if (!message.isDeleted && message.parentMessage != null) { + val parentChatMessage = message.parentMessage + parentChatMessage!!.activeUser = message.activeUser + parentChatMessage.imageUrl?.let { + binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE + binding.messageQuote.quotedMessageImage.load(it) { + addHeader( + "Authorization", + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ) + } + } ?: run { + binding.messageQuote.quotedMessageImage.visibility = View.GONE + } + binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName + ?: context!!.getText(R.string.nc_nick_guest) + binding.messageQuote.quotedMessage.text = parentChatMessage.text + binding.messageQuote.quotedMessage.setTextColor( + context!!.resources.getColor(R.color.nc_outcoming_text_default) + ) + binding.messageQuote.quotedMessageAuthor.setTextColor(context!!.resources.getColor(R.color.nc_grey)) + + binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white) + + binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE + } else { + binding.messageQuote.quotedChatMessageView.visibility = View.GONE + } + } + + private fun colorizeMessageBubble(message: ChatMessage) { + val resources = sharedApplication!!.resources + val bgBubbleColor = if (message.isDeleted) { + resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted) + } else { + resources.getColor(R.color.bg_message_list_outcoming_bubble) + } + if (message.isGrouped) { + val bubbleDrawable = DisplayUtils.getMessageSelector( + bgBubbleColor, + resources.getColor(R.color.transparent), + bgBubbleColor, + R.drawable.shape_grouped_outcoming_message + ) + ViewCompat.setBackground(bubble, bubbleDrawable) + } else { + val bubbleDrawable = DisplayUtils.getMessageSelector( + bgBubbleColor, + resources.getColor(R.color.transparent), + bgBubbleColor, + R.drawable.shape_outcoming_message + ) + ViewCompat.setBackground(bubble, bubbleDrawable) + } + } + + fun assignReactionInterface(reactionsInterface: ReactionsInterface) { + this.reactionsInterface = reactionsInterface + } + + companion object { + private val TAG = NextcloudTalkApplication::class.java.simpleName + } +} 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 6f2b65e3d..e6f0b8d97 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -1,5 +1,5 @@ /* - * Nextcloud Talk application + * Nextcloud Talk application * * @author Mario Danic * @author Marcel Hibbe @@ -47,6 +47,7 @@ import com.nextcloud.talk.models.json.statuses.StatusesOverall; import com.nextcloud.talk.models.json.unifiedsearch.UnifiedSearchOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; +import com.nextcloud.talk.polls.repositories.model.PollOverall; import java.util.List; import java.util.Map; @@ -526,4 +527,20 @@ public interface NcApi { @Query("from") String fromUrl, @Query("limit") Integer limit, @Query("cursor") Integer cursor); + + @GET + Observable getPoll(@Header("Authorization") String authorization, + @Url String url); + + @POST + Observable createPoll(@Header("Authorization") String authorization, + @Url String url); + + @POST + Observable votePoll(@Header("Authorization") String authorization, + @Url String url); + + @DELETE + Observable closePoll(@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 15c506670..dbe202e39 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -104,6 +104,7 @@ import com.nextcloud.talk.activities.CallActivity import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.activities.TakePhotoActivity import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder +import com.nextcloud.talk.adapters.messages.IncomingPollMessageViewHolder import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder import com.nextcloud.talk.adapters.messages.IncomingVoiceMessageViewHolder import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder @@ -111,6 +112,7 @@ import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder import com.nextcloud.talk.adapters.messages.OutcomingLocationMessageViewHolder +import com.nextcloud.talk.adapters.messages.OutcomingPollMessageViewHolder import com.nextcloud.talk.adapters.messages.OutcomingPreviewMessageViewHolder import com.nextcloud.talk.adapters.messages.OutcomingVoiceMessageViewHolder import com.nextcloud.talk.adapters.messages.PreviewMessageInterface @@ -544,6 +546,17 @@ class ChatController(args: Bundle) : this ) + messageHolders.registerContentType( + CONTENT_TYPE_POLL, + IncomingPollMessageViewHolder::class.java, + profileBottomSheet, + R.layout.item_custom_incoming_poll_message, + OutcomingPollMessageViewHolder::class.java, + null, + R.layout.item_custom_outcoming_poll_message, + this + ) + var senderId = "" if (!conversationUser?.userId.equals("?")) { senderId = "users/" + conversationUser?.userId @@ -3012,6 +3025,7 @@ class ChatController(args: Bundle) : return when (type) { CONTENT_TYPE_LOCATION -> message.hasGeoLocation() CONTENT_TYPE_VOICE_MESSAGE -> message.isVoiceMessage + CONTENT_TYPE_POLL -> message.isPoll() CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage) CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1" else -> false @@ -3127,6 +3141,7 @@ class ChatController(args: Bundle) : private const val CONTENT_TYPE_UNREAD_NOTICE_MESSAGE: Byte = 2 private const val CONTENT_TYPE_LOCATION: Byte = 3 private const val CONTENT_TYPE_VOICE_MESSAGE: Byte = 4 + private const val CONTENT_TYPE_POLL: Byte = 5 private const val NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200 private const val POP_CURRENT_CONTROLLER_DELAY: Long = 100 private const val LOBBY_TIMER_DELAY: Long = 5000 diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index 215c3b3f0..faa0f1e50 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt @@ -124,6 +124,8 @@ data class ChatMessage( var voiceMessageDownloadProgress: Int = 0, ) : Parcelable, MessageContentType, MessageContentType.Image { + + // TODO: messageTypesToIgnore is weird. must be deleted by refactoring! @JsonIgnore var messageTypesToIgnore = Arrays.asList( MessageType.REGULAR_TEXT_MESSAGE, @@ -132,7 +134,8 @@ data class ChatMessage( MessageType.SINGLE_LINK_AUDIO_MESSAGE, MessageType.SINGLE_LINK_MESSAGE, MessageType.SINGLE_NC_GEOLOCATION_MESSAGE, - MessageType.VOICE_MESSAGE + MessageType.VOICE_MESSAGE, + MessageType.POLL_MESSAGE ) fun hasFileAttachment(): Boolean { @@ -165,6 +168,21 @@ data class ChatMessage( return false } + fun isPoll(): Boolean { + if (messageParameters != null && messageParameters!!.size > 0) { + for ((_, individualHashMap) in messageParameters!!) { + if (MessageDigest.isEqual( + individualHashMap["type"]!!.toByteArray(), + "talk-poll".toByteArray() + ) + ) { + return true + } + } + } + return false + } + override fun getImageUrl(): String? { if (messageParameters != null && messageParameters!!.size > 0) { for ((_, individualHashMap) in messageParameters!!) { @@ -207,6 +225,8 @@ data class ChatMessage( MessageType.SINGLE_NC_ATTACHMENT_MESSAGE } else if (hasGeoLocation()) { MessageType.SINGLE_NC_GEOLOCATION_MESSAGE + } else if (isPoll()) { + MessageType.POLL_MESSAGE } else { MessageType.REGULAR_TEXT_MESSAGE } @@ -334,6 +354,15 @@ data class ChatMessage( getNullsafeActorDisplayName() ) } + } else if (MessageType.POLL_MESSAGE == getCalculateMessageType()) { + return if (actorId == activeUser!!.userId) { + sharedApplication!!.getString(R.string.nc_sent_poll_you) + } else { + String.format( + sharedApplication!!.resources.getString(R.string.nc_sent_an_image), + getNullsafeActorDisplayName() + ) + } } } return "" @@ -410,6 +439,7 @@ data class ChatMessage( SINGLE_LINK_AUDIO_MESSAGE, SINGLE_NC_ATTACHMENT_MESSAGE, SINGLE_NC_GEOLOCATION_MESSAGE, + POLL_MESSAGE, VOICE_MESSAGE } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt new file mode 100644 index 000000000..a61bc2630 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt @@ -0,0 +1,65 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.repositories.model + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class Poll( + @JsonField(name = ["id"]) + var id: Int = 0, + + @JsonField(name = ["question"]) + var question: String? = null, + + @JsonField(name = ["options"]) + var options: ArrayList? = null, + + @JsonField(name = ["votes"]) + var votes: ArrayList? = null, + + @JsonField(name = ["actorType"]) + var actorType: String? = null, + + @JsonField(name = ["actorId"]) + var actorId: String? = null, + + @JsonField(name = ["actorDisplayName"]) + var actorDisplayName: String? = null, + + @JsonField(name = ["status"]) + var status: Int = 0, + + @JsonField(name = ["resultMode"]) + var resultMode: Int = 0, + + @JsonField(name = ["maxVotes"]) + var maxVotes: Int = 0, + + @JsonField(name = ["votedSelf"]) + var votedSelf: ArrayList? = null, +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(0, null, null, null, null, null, null, 0, 0, 0, null) +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt new file mode 100644 index 000000000..d2bd27b06 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.repositories.model + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class PollOCS( + @JsonField(name = ["data"]) + var data: Poll? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOverall.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOverall.kt new file mode 100644 index 000000000..d5b8fb331 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOverall.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.repositories.model + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class PollOverall( + @JsonField(name = ["ocs"]) + var ocs: PollOCS? = null +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} 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 5c999ed0e..d38cbcb12 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -2,8 +2,10 @@ * Nextcloud Talk application * * @author Mario Danic + * @author Marcel Hibbe * @author Tim Krüger * Copyright (C) 2021 Tim Krüger + * Copyright (C) 2021-2022 Marcel Hibbe * Copyright (C) 2017-2018 Mario Danic * * This program is free software: you can redistribute it and/or modify @@ -61,8 +63,8 @@ public class ApiUtils { } /** - * @deprecated This is only supported on API v1-3, in API v4+ please use - * {@link ApiUtils#getUrlForAttendees(int, String, String)} instead. + * @deprecated This is only supported on API v1-3, in API v4+ please use {@link ApiUtils#getUrlForAttendees(int, + * String, String)} instead. */ @Deprecated public static String getUrlForRemovingParticipantFromConversation(String baseUrl, String roomToken, boolean isGuest) { @@ -95,13 +97,13 @@ public class ApiUtils { public static String getUrlForFilePreviewWithRemotePath(String baseUrl, String remotePath, int px) { return baseUrl + "/index.php/core/preview.png?file=" - + Uri.encode(remotePath, "UTF-8") - + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; + + Uri.encode(remotePath, "UTF-8") + + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; } public static String getUrlForFilePreviewWithFileId(String baseUrl, String fileId, int px) { return baseUrl + "/index.php/core/preview?fileId=" - + fileId + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; + + fileId + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; } public static String getSharingUrl(String baseUrl) { @@ -151,8 +153,8 @@ public class ApiUtils { if (user.hasSpreedFeatureCapability("conversation-v2")) { return version; } - if (version == APIv1 && - user.hasSpreedFeatureCapability("mention-flag") && + if (version == APIv1 && + user.hasSpreedFeatureCapability( "mention-flag") && !user.hasSpreedFeatureCapability("conversation-v4")) { return version; } @@ -238,7 +240,7 @@ public class ApiUtils { } public static String getUrlForParticipants(int version, String baseUrl, String token) { - if (token == null || token.isEmpty()){ + if (token == null || token.isEmpty()) { Log.e(TAG, "token was null or empty"); } return getUrlForRoom(version, baseUrl, token) + "/participants"; @@ -287,6 +289,7 @@ public class ApiUtils { public static String getUrlForCall(int version, String baseUrl, String token) { return getUrlForApi(version, baseUrl) + "/call/" + token; } + public static String getUrlForChat(int version, String baseUrl, String token) { return getUrlForApi(version, baseUrl) + "/chat/" + token; } @@ -294,10 +297,11 @@ public class ApiUtils { public static String getUrlForMentionSuggestions(int version, String baseUrl, String token) { return getUrlForChat(version, baseUrl, token) + "/mentions"; } + public static String getUrlForChatMessage(int version, String baseUrl, String token, String messageId) { return getUrlForChat(version, baseUrl, token) + "/" + messageId; } - + public static String getUrlForChatSharedItems(int version, String baseUrl, String token) { return getUrlForChat(version, baseUrl, token) + "/share"; } @@ -366,11 +370,11 @@ public class ApiUtils { } public static RetrofitBucket getRetrofitBucketForAddParticipantWithSource( - int version, - String baseUrl, - String token, - String source, - String id + int version, + String baseUrl, + String token, + String source, + String id ) { RetrofitBucket retrofitBucket = getRetrofitBucketForAddParticipant(version, baseUrl, token, id); retrofitBucket.getQueryMap().put("source", source); @@ -417,7 +421,7 @@ public class ApiUtils { public static String getUrlPushProxy() { return NextcloudTalkApplication.Companion.getSharedApplication(). - getApplicationContext().getResources().getString(R.string.nc_push_server_url) + "/devices"; + getApplicationContext().getResources().getString(R.string.nc_push_server_url) + "/devices"; } public static String getUrlForNotificationWithId(String baseUrl, String notificationId) { @@ -448,8 +452,10 @@ public class ApiUtils { return getUrlForChat(version, baseUrl, roomToken) + "/share"; } - public static String getUrlForHoverCard(String baseUrl, String userId) { return baseUrl + ocsApiVersion + - "/hovercard/v1/" + userId; } + public static String getUrlForHoverCard(String baseUrl, String userId) { + return baseUrl + ocsApiVersion + + "/hovercard/v1/" + userId; + } public static String getUrlForSetChatReadMarker(int version, String baseUrl, String roomToken) { return getUrlForChat(version, baseUrl, roomToken) + "/read"; @@ -497,4 +503,16 @@ public class ApiUtils { public static String getUrlForUnifiedSearch(@NotNull String baseUrl, @NotNull String providerId) { return baseUrl + ocsApiVersion + "/search/providers/" + providerId + "/search"; } + + public static String getUrlForPoll(String baseUrl, + String roomToken, + String pollId) { + return getUrlForPoll(baseUrl, roomToken) + "/" + pollId; + } + + public static String getUrlForPoll(String baseUrl, + String roomToken) { + return baseUrl + ocsApiVersion + spreedApiVersion + "/poll/" + roomToken; + } + } diff --git a/app/src/main/res/drawable/ic_baseline_bar_chart_24.xml b/app/src/main/res/drawable/ic_baseline_bar_chart_24.xml new file mode 100644 index 000000000..b84f89296 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_bar_chart_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/item_custom_incoming_poll_message.xml b/app/src/main/res/layout/item_custom_incoming_poll_message.xml new file mode 100644 index 000000000..7f198bd15 --- /dev/null +++ b/app/src/main/res/layout/item_custom_incoming_poll_message.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_custom_outcoming_poll_message.xml b/app/src/main/res/layout/item_custom_outcoming_poll_message.xml new file mode 100644 index 000000000..0a52ee34b --- /dev/null +++ b/app/src/main/res/layout/item_custom_outcoming_poll_message.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f6acc81cd..5ef3f0bd7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -309,6 +309,7 @@ %1$s sent an audio. %1$s sent a video. %1$s sent an image. + %1$s sent a poll. %1$s sent a location. %1$s sent a voice message. You sent a link. @@ -317,6 +318,7 @@ You sent an audio. You sent a video. You sent an image. + You sent a poll. You sent a location. You sent a voice message. %1$s: %2$s @@ -435,6 +437,10 @@ Play/pause voice message Permission for audio recording is required + + Tap to vote + Tap to see results + phone_book_integration Match contacts based on phone number to integrate Talk shortcut into system contacts app @@ -534,4 +540,5 @@ Call without notification Set avatar from camera + From bf0544556122647988ef6f8a1014e33ba3282eb4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 3 Jun 2022 15:21:18 +0200 Subject: [PATCH 02/92] open poll dialog Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 48 +++++++---- .../nextcloud/talk/polls/model/PollModel.kt | 4 + .../repositories/DialogPollRepository.kt | 12 +++ .../talk/polls/ui/PollVoteDialogFragment.kt | 83 +++++++++++++++++++ .../talk/polls/viewmodels/PollViewModel.kt | 34 ++++++++ app/src/main/res/layout/dialog_poll_vote.xml | 81 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 7 files changed, 249 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt create mode 100644 app/src/main/res/layout/dialog_poll_vote.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 8ada42208..2aa1cf541 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -34,11 +34,13 @@ import autodagger.AutoInjector import coil.load import com.amulyakhare.textdrawable.TextDrawable import com.nextcloud.talk.R +import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.databinding.ItemCustomIncomingPollMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.polls.ui.PollVoteDialogFragment import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils @@ -98,30 +100,48 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH } private fun setPollPreview(message: ChatMessage) { - var pollId: String? - var pollName: String? = "" + var pollId: Int? = null + var pollName: String? = null if (message.messageParameters != null && message.messageParameters!!.size > 0) { for (key in message.messageParameters!!.keys) { val individualHashMap: Map = message.messageParameters!![key]!! if (individualHashMap["type"] == "talk-poll") { - pollId = individualHashMap["id"] - pollName = individualHashMap["name"] + pollId = Integer.parseInt(individualHashMap["id"]) + pollName = individualHashMap["name"].toString() } } } - binding.messagePollTitle.text = pollName + if (pollId != null && pollName != null) { + binding.messagePollTitle.text = pollName - // TODO: how to get room token here? - // val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) - // ncApi!!.getPoll( - // credentials, - // ApiUtils.getUrlForPoll( - // message.activeUser?.baseUrl, - // ??????? - // ) - // ) + // TODO: how to get room token here? + val roomToken = "???????????????????????????" + + + binding.bubble.setOnClickListener { + val pollVoteDialog = PollVoteDialogFragment.newInstance( + message.activeUser!!, roomToken, pollId, + pollName + ) + pollVoteDialog.show( + (binding.messagePollIcon.context as MainActivity).supportFragmentManager, + TAG + ) + } + + // wait for https://github.com/nextcloud/spreed/pull/7306#issuecomment-1145819317 + + // val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) + // ncApi!!.getPoll( + // credentials, + // ApiUtils.getUrlForPoll( + // message.activeUser?.baseUrl, + // ??????? + // ) + // ) + } } private fun setAvatarAndAuthorOnMessageItem(message: ChatMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt b/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt new file mode 100644 index 000000000..b8179c97a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt @@ -0,0 +1,4 @@ +package com.nextcloud.talk.polls.model + +class PollModel { +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt new file mode 100644 index 000000000..b9c9c9a4c --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt @@ -0,0 +1,12 @@ +package com.nextcloud.talk.polls.repositories + +interface DialogPollRepository { + + data class Parameters( + val userName: String, + val userToken: String, + val baseUrl: String, + val roomToken: String, + val pollId: Int + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt new file mode 100644 index 000000000..76ee50e21 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt @@ -0,0 +1,83 @@ +package com.nextcloud.talk.polls.ui + +import android.annotation.SuppressLint +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import autodagger.AutoInjector +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.databinding.DialogPollVoteBinding +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.polls.viewmodels.PollViewModel + +var user: UserEntity? = null +var pollId: Int? = null +var roomToken: String? = null +var pollTitle: String? = null + +@AutoInjector(NextcloudTalkApplication::class) +class PollVoteDialogFragment : DialogFragment() { + + private lateinit var binding: DialogPollVoteBinding + private lateinit var viewModel: PollViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + } + + @SuppressLint("InflateParams") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogPollVoteBinding.inflate(LayoutInflater.from(context)) + + return AlertDialog.Builder(requireContext()) + .setView(binding.root) + .create() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return binding.root + } + + @SuppressLint("DefaultLocale") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.messagePollTitle.text = pollTitle + + + + viewModel.viewState.observe(this) { state -> + // when (state) { + // } + } + + viewModel.initialize(user!!, roomToken!!, pollId!!) + } + + /** + * Fragment creator + */ + companion object { + @JvmStatic + fun newInstance( + userEntity: UserEntity, + roomTokenParam: String, + id: Int, + name: String + ): PollVoteDialogFragment { + user = userEntity // TODO replace with "putParcelable" like in SetStatusDialogFragment??? + roomToken = roomTokenParam + pollId = id + pollTitle = name + + val dialogFragment = PollVoteDialogFragment() + return dialogFragment + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt new file mode 100644 index 000000000..04c9cbbe2 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt @@ -0,0 +1,34 @@ +package com.nextcloud.talk.polls.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.polls.model.PollModel +import com.nextcloud.talk.polls.repositories.DialogPollRepository +import javax.inject.Inject + +class PollViewModel @Inject constructor(private val repository: DialogPollRepository) : ViewModel() { + + private lateinit var repositoryParameters: DialogPollRepository.Parameters + + sealed interface ViewState + object InitialState : ViewState + open class PollOpenState(val poll: PollModel) : ViewState + open class PollClosedState(val poll: PollModel) : ViewState + + private val _viewState: MutableLiveData = MutableLiveData(InitialState) + val viewState: LiveData + get() = _viewState + + fun initialize(userEntity: UserEntity, roomToken: String, pollId: Int) { + repositoryParameters = DialogPollRepository.Parameters( + userEntity.userId, + userEntity.token, + userEntity.baseUrl, + roomToken, + pollId + ) + // loadAvailableTypes() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml new file mode 100644 index 000000000..8ff72b000 --- /dev/null +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ef3f0bd7..131b6bb5f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,6 +27,7 @@ Skip Set Sorry, something went wrong! + Submit Settings From e605546925aaffff508c24efc93544bf490a09aa Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 7 Jun 2022 11:11:24 +0200 Subject: [PATCH 03/92] fix klint warnings Signed-off-by: Marcel Hibbe --- .../talk/adapters/messages/IncomingPollMessageViewHolder.kt | 1 - app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt | 2 +- .../nextcloud/talk/polls/repositories/DialogPollRepository.kt | 2 +- .../java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt | 2 -- .../java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt | 2 +- 5 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 2aa1cf541..e3db349a4 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -119,7 +119,6 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH // TODO: how to get room token here? val roomToken = "???????????????????????????" - binding.bubble.setOnClickListener { val pollVoteDialog = PollVoteDialogFragment.newInstance( message.activeUser!!, roomToken, pollId, diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt b/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt index b8179c97a..599189bd9 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt @@ -1,4 +1,4 @@ package com.nextcloud.talk.polls.model class PollModel { -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt index b9c9c9a4c..673600108 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt @@ -9,4 +9,4 @@ interface DialogPollRepository { val roomToken: String, val pollId: Int ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt index 76ee50e21..6a6157587 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt @@ -50,8 +50,6 @@ class PollVoteDialogFragment : DialogFragment() { binding.messagePollTitle.text = pollTitle - - viewModel.viewState.observe(this) { state -> // when (state) { // } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt index 04c9cbbe2..424ef7fd9 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt @@ -31,4 +31,4 @@ class PollViewModel @Inject constructor(private val repository: DialogPollReposi ) // loadAvailableTypes() } -} \ No newline at end of file +} From 01f18d7eca810f90fac1ba0beabf703e470f8aef Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 7 Jun 2022 11:11:43 +0200 Subject: [PATCH 04/92] add API poll details Signed-off-by: Marcel Hibbe --- .../talk/polls/repositories/model/Poll.kt | 9 ++++++- .../polls/repositories/model/PollDetails.kt | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt index a61bc2630..33f273dbc 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt @@ -59,7 +59,14 @@ data class Poll( @JsonField(name = ["votedSelf"]) var votedSelf: ArrayList? = null, -) : Parcelable { + + @JsonField(name = ["numVoters"]) + var numVoters: Int = 0, + + @JsonField(name = ["details"]) + var details: ArrayList? = null, + + ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(0, null, null, null, null, null, null, 0, 0, 0, null) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt new file mode 100644 index 000000000..16c1bddac --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt @@ -0,0 +1,26 @@ +package com.nextcloud.talk.polls.repositories.model + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.android.parcel.Parcelize + +@Parcelize +@JsonObject +data class PollDetails( + @JsonField(name = ["actorType"]) + var actorType: String? = null, + + @JsonField(name = ["actorId"]) + var actorId: String? = null, + + @JsonField(name = ["actorDisplayName"]) + var actorDisplayName: String? = null, + + @JsonField(name = ["optionId"]) + var optionId: Int? = 0, + + ) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null, null, 0) +} From 42324419cdceb88bb78466993a8e5f3f637da7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Brey?= Date: Tue, 7 Jun 2022 17:21:10 +0200 Subject: [PATCH 05/92] wip: Poll view model work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Álvaro Brey --- .../messages/IncomingPollMessageViewHolder.kt | 9 +- .../talk/dagger/modules/RepositoryModule.kt | 7 ++ .../talk/dagger/modules/ViewModelModule.kt | 12 +++ .../com/nextcloud/talk/polls/model/Poll.kt | 20 ++++ .../nextcloud/talk/polls/model/PollModel.kt | 4 - .../repositories/DialogPollRepository.kt | 12 --- .../talk/polls/repositories/PollRepository.kt | 9 ++ .../polls/repositories/PollRepositoryImpl.kt | 52 ++++++++++ .../polls/repositories/model/PollDetails.kt | 2 +- .../talk/polls/repositories/model/PollOCS.kt | 2 +- .../model/{Poll.kt => PollResponse.kt} | 4 +- ...gFragment.kt => PollMainDialogFragment.kt} | 74 ++++++++------- .../talk/polls/ui/PollVoteFragment.kt | 95 +++++++++++++++++++ .../talk/polls/viewmodels/PollViewModel.kt | 58 +++++++---- .../polls/viewmodels/PollVoteViewModel.kt | 37 ++++++++ app/src/main/res/layout/dialog_poll_main.xml | 58 +++++++++++ app/src/main/res/layout/dialog_poll_vote.xml | 50 ++-------- 17 files changed, 388 insertions(+), 117 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt rename app/src/main/java/com/nextcloud/talk/polls/repositories/model/{Poll.kt => PollResponse.kt} (98%) rename app/src/main/java/com/nextcloud/talk/polls/ui/{PollVoteDialogFragment.kt => PollMainDialogFragment.kt} (51%) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt create mode 100644 app/src/main/res/layout/dialog_poll_main.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index e3db349a4..5bcd9ed83 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -40,7 +40,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.databinding.ItemCustomIncomingPollMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage -import com.nextcloud.talk.polls.ui.PollVoteDialogFragment +import com.nextcloud.talk.polls.ui.PollMainDialogFragment import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils @@ -100,14 +100,14 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH } private fun setPollPreview(message: ChatMessage) { - var pollId: Int? = null + var pollId: String? = null var pollName: String? = null if (message.messageParameters != null && message.messageParameters!!.size > 0) { for (key in message.messageParameters!!.keys) { val individualHashMap: Map = message.messageParameters!![key]!! if (individualHashMap["type"] == "talk-poll") { - pollId = Integer.parseInt(individualHashMap["id"]) + pollId = individualHashMap["id"] pollName = individualHashMap["name"].toString() } } @@ -120,7 +120,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH val roomToken = "???????????????????????????" binding.bubble.setOnClickListener { - val pollVoteDialog = PollVoteDialogFragment.newInstance( + val pollVoteDialog = PollMainDialogFragment.newInstance( message.activeUser!!, roomToken, pollId, pollName ) @@ -130,6 +130,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH ) } + // TODO get poll from api // wait for https://github.com/nextcloud/spreed/pull/7306#issuecomment-1145819317 // val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt index bb8a5f255..55d9580aa 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt @@ -24,6 +24,8 @@ package com.nextcloud.talk.dagger.modules import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.polls.repositories.PollRepository +import com.nextcloud.talk.polls.repositories.PollRepositoryImpl import com.nextcloud.talk.data.source.local.TalkDatabase import com.nextcloud.talk.data.storage.ArbitraryStoragesRepository import com.nextcloud.talk.data.storage.ArbitraryStoragesRepositoryImpl @@ -52,6 +54,11 @@ class RepositoryModule { return UnifiedSearchRepositoryImpl(ncApi, userProvider) } + @Provides + fun provideDialogPollRepository(ncApi: NcApi, userProvider: CurrentUserProvider): PollRepository { + return PollRepositoryImpl(ncApi, userProvider) + } + @Provides fun provideRemoteFileBrowserItemsRepository(okHttpClient: OkHttpClient, userProvider: CurrentUserProvider): RemoteFileBrowserItemsRepository { diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index f2356d0ab..cfccd7a23 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -25,6 +25,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel import com.nextcloud.talk.messagesearch.MessageSearchViewModel +import com.nextcloud.talk.polls.viewmodels.PollViewModel +import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel import dagger.Binds import dagger.MapKey @@ -61,6 +63,16 @@ abstract class ViewModelModule { @ViewModelKey(MessageSearchViewModel::class) abstract fun messageSearchViewModel(viewModel: MessageSearchViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(PollViewModel::class) + abstract fun pollViewModel(viewModel: PollViewModel): ViewModel + + @Binds + @IntoMap + @ViewModelKey(PollVoteViewModel::class) + abstract fun pollVoteViewModel(viewModel: PollVoteViewModel): ViewModel + @Binds @IntoMap @ViewModelKey(RemoteFileBrowserItemsViewModel::class) diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt new file mode 100644 index 000000000..ddad6a7ad --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt @@ -0,0 +1,20 @@ +package com.nextcloud.talk.polls.model + +import com.nextcloud.talk.polls.repositories.model.PollDetails + +data class Poll( + val id: String, + val question: String?, + val options: List?, + val votes: List?, + val actorType: String?, + val actorId: String?, + val actorDisplayName: String?, + val status: Int, + val resultMode: Int, + val maxVotes: Int, + val votedSelf: List?, + val numVoters: Int, + // TODO PollDetails needs own model class + val details: List? +) diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt b/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt deleted file mode 100644 index 599189bd9..000000000 --- a/app/src/main/java/com/nextcloud/talk/polls/model/PollModel.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.nextcloud.talk.polls.model - -class PollModel { -} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt deleted file mode 100644 index 673600108..000000000 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/DialogPollRepository.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.nextcloud.talk.polls.repositories - -interface DialogPollRepository { - - data class Parameters( - val userName: String, - val userToken: String, - val baseUrl: String, - val roomToken: String, - val pollId: Int - ) -} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt new file mode 100644 index 000000000..45d1acfbe --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -0,0 +1,9 @@ +package com.nextcloud.talk.polls.repositories + +import com.nextcloud.talk.polls.model.Poll +import io.reactivex.Observable + +interface PollRepository { + + fun getPoll(roomToken: String, pollId: String): Observable +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt new file mode 100644 index 000000000..348f7d767 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -0,0 +1,52 @@ +/* + * Nextcloud Talk application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * 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 . + */ + +package com.nextcloud.talk.polls.repositories + +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.polls.model.Poll +import com.nextcloud.talk.utils.database.user.CurrentUserProvider +import io.reactivex.Observable + +class PollRepositoryImpl(private val api: NcApi, private val currentUserProvider: CurrentUserProvider) : + PollRepository { + + override fun getPoll(roomToken: String, pollId: String): Observable { + // TODO actual api call + return Observable.just( + Poll( + id = "aaa", + question = "what if?", + options = listOf("yes", "no", "maybe", "I don't know"), + votes = listOf(0, 0, 0, 0), + actorType = "", + actorId = "", + actorDisplayName = "", + status = 0, + resultMode = 0, + maxVotes = 1, + votedSelf = listOf(0, 0, 0, 0), + numVoters = 0, + details = emptyList() + ) + ) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt index 16c1bddac..73f8fd21e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt @@ -20,7 +20,7 @@ data class PollDetails( @JsonField(name = ["optionId"]) var optionId: Int? = 0, - ) : Parcelable { +) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null, null, 0) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt index d2bd27b06..1adf3832b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollOCS.kt @@ -28,7 +28,7 @@ import kotlinx.android.parcel.Parcelize @JsonObject data class PollOCS( @JsonField(name = ["data"]) - var data: Poll? + var data: PollResponse? ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt similarity index 98% rename from app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt rename to app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt index 33f273dbc..7aabfe12a 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt @@ -26,7 +26,7 @@ import kotlinx.android.parcel.Parcelize @Parcelize @JsonObject -data class Poll( +data class PollResponse( @JsonField(name = ["id"]) var id: Int = 0, @@ -66,7 +66,7 @@ data class Poll( @JsonField(name = ["details"]) var details: ArrayList? = null, - ) : Parcelable { +) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(0, null, null, null, null, null, null, 0, 0, 0, null) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt similarity index 51% rename from app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt rename to app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 6a6157587..e2c6a9191 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -8,54 +8,68 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment +import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.databinding.DialogPollVoteBinding +import com.nextcloud.talk.databinding.DialogPollMainBinding import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.polls.viewmodels.PollViewModel - -var user: UserEntity? = null -var pollId: Int? = null -var roomToken: String? = null -var pollTitle: String? = null +import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollVoteDialogFragment : DialogFragment() { +class PollMainDialogFragment( + private val pollId: String, + private val roomToken: String, + private val pollTitle: String +) : DialogFragment() { - private lateinit var binding: DialogPollVoteBinding + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + private lateinit var binding: DialogPollMainBinding private lateinit var viewModel: PollViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + + viewModel = ViewModelProvider(this, viewModelFactory)[PollViewModel::class.java] } @SuppressLint("InflateParams") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - binding = DialogPollVoteBinding.inflate(LayoutInflater.from(context)) + binding = DialogPollMainBinding.inflate(LayoutInflater.from(context)) - return AlertDialog.Builder(requireContext()) + val dialog = AlertDialog.Builder(requireContext()) .setView(binding.root) .create() - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return binding.root - } - - @SuppressLint("DefaultLocale") - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) binding.messagePollTitle.text = pollTitle - viewModel.viewState.observe(this) { state -> - // when (state) { - // } + return dialog + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.viewState.observe(viewLifecycleOwner) { state -> + when (state) { + PollViewModel.InitialState -> {} + is PollViewModel.PollClosedState -> TODO() + is PollViewModel.PollOpenState -> { + val contentFragment = PollVoteFragment(viewModel) + val transaction = childFragmentManager.beginTransaction() + transaction.replace(binding.messagePollContentFragment.id, contentFragment) + transaction.commit() + } + } } - viewModel.initialize(user!!, roomToken!!, pollId!!) + viewModel.initialize(roomToken, pollId) } /** @@ -66,16 +80,8 @@ class PollVoteDialogFragment : DialogFragment() { fun newInstance( userEntity: UserEntity, roomTokenParam: String, - id: Int, + pollId: String, name: String - ): PollVoteDialogFragment { - user = userEntity // TODO replace with "putParcelable" like in SetStatusDialogFragment??? - roomToken = roomTokenParam - pollId = id - pollTitle = name - - val dialogFragment = PollVoteDialogFragment() - return dialogFragment - } + ): PollMainDialogFragment = PollMainDialogFragment(pollId, roomTokenParam, name) } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt new file mode 100644 index 000000000..fec941504 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -0,0 +1,95 @@ +/* + * Nextcloud Talk application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * 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 . + */ + +package com.nextcloud.talk.polls.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.RadioButton +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import autodagger.AutoInjector +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.databinding.DialogPollVoteBinding +import com.nextcloud.talk.polls.viewmodels.PollViewModel +import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class PollVoteFragment(private val parentViewModel: PollViewModel) : Fragment() { + + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + lateinit var viewModel: PollVoteViewModel + + var _binding: DialogPollVoteBinding? = null + val binding: DialogPollVoteBinding + get() = _binding!! + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + viewModel = ViewModelProvider(this, viewModelFactory)[PollVoteViewModel::class.java] + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = DialogPollVoteBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + parentViewModel.viewState.observe(viewLifecycleOwner) { state -> + if (state is PollViewModel.PollOpenState) { + val poll = state.poll + binding.radioGroup.removeAllViews() + poll.options?.map { option -> + RadioButton(context) + .apply { text = option } + .also { + it.setOnClickListener { + // todo + } + } + }?.forEach { + binding.radioGroup.addView(it) + } + } + } + binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> + // todo set selected in viewmodel + } + // todo observe viewmodel checked, set view checked with it + // todo listen to button click, submit + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt index 424ef7fd9..4ed59ab42 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt @@ -3,32 +3,58 @@ package com.nextcloud.talk.polls.viewmodels import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.nextcloud.talk.models.database.UserEntity -import com.nextcloud.talk.polls.model.PollModel -import com.nextcloud.talk.polls.repositories.DialogPollRepository +import com.nextcloud.talk.polls.model.Poll +import com.nextcloud.talk.polls.repositories.PollRepository +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class PollViewModel @Inject constructor(private val repository: DialogPollRepository) : ViewModel() { +/** + * @startuml + * hide empty description + * [*] --> InitialState + * InitialState --> PollOpenState + * note left + * Open second viewmodel for child fragment + * end note + * InitialState --> PollClosedState + * @enduml + */ +class PollViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { - private lateinit var repositoryParameters: DialogPollRepository.Parameters + private lateinit var roomToken: String + private lateinit var pollId: String sealed interface ViewState object InitialState : ViewState - open class PollOpenState(val poll: PollModel) : ViewState - open class PollClosedState(val poll: PollModel) : ViewState + open class PollOpenState(val poll: Poll) : ViewState + open class PollClosedState(val poll: Poll) : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) val viewState: LiveData get() = _viewState - fun initialize(userEntity: UserEntity, roomToken: String, pollId: Int) { - repositoryParameters = DialogPollRepository.Parameters( - userEntity.userId, - userEntity.token, - userEntity.baseUrl, - roomToken, - pollId - ) - // loadAvailableTypes() + private var disposable: Disposable? = null + + fun initialize(roomToken: String, pollId: String) { + this.roomToken = roomToken + this.pollId = pollId + + loadPoll() + } + + private fun loadPoll() { + disposable = repository.getPoll(roomToken, pollId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { poll -> + _viewState.value = PollOpenState(poll) + } + } + + override fun onCleared() { + super.onCleared() + disposable?.dispose() } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt new file mode 100644 index 000000000..40bb6594d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -0,0 +1,37 @@ +/* + * Nextcloud Talk application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * 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 . + */ + +package com.nextcloud.talk.polls.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import javax.inject.Inject + +class PollVoteViewModel @Inject constructor() : ViewModel() { + private val _selectedOptions: MutableLiveData> = MutableLiveData(emptyList()) + val selectedOptions: LiveData> + get() = _selectedOptions + + fun selectOption(option: String) { + _selectedOptions.value = listOf(option) + } +} diff --git a/app/src/main/res/layout/dialog_poll_main.xml b/app/src/main/res/layout/dialog_poll_main.xml new file mode 100644 index 000000000..f4c5e2b62 --- /dev/null +++ b/app/src/main/res/layout/dialog_poll_main.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 8ff72b000..094d756ce 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -20,51 +20,15 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - xmlns:tools="http://schemas.android.com/tools"> - - - - + xmlns:tools="http://schemas.android.com/tools" + tools:background="@color/white"> - - - - + app:layout_constraintTop_toTopOf="parent"> @@ -72,10 +36,10 @@ android:id="@+id/setStatus" android:layout_width="wrap_content" android:layout_height="wrap_content" + app:cornerRadius="@dimen/button_corner_radius" + app:layout_constraintEnd_toEndOf="parent" android:text="@string/nc_common_submit" android:theme="@style/Button.Primary" - app:cornerRadius="@dimen/button_corner_radius" - app:layout_constraintEnd_toEndOf="@+id/message_poll_title" app:layout_constraintTop_toBottomOf="@+id/radioGroup" /> From c7e97218091a89383302d4ed13c5cc7ee04f2caa Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 8 Jun 2022 15:48:05 +0200 Subject: [PATCH 06/92] wip: get poll from API Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 6 +- .../com/nextcloud/talk/polls/model/Poll.kt | 3 - .../nextcloud/talk/polls/model/PollDetails.kt | 8 ++ .../talk/polls/repositories/PollRepository.kt | 2 +- .../polls/repositories/PollRepositoryImpl.kt | 88 +++++++++++++++---- ...{PollDetails.kt => PollDetailsResponse.kt} | 12 +-- .../polls/repositories/model/PollResponse.kt | 8 +- .../talk/polls/ui/PollMainDialogFragment.kt | 2 - .../talk/polls/ui/PollVoteFragment.kt | 3 + .../talk/polls/viewmodels/PollViewModel.kt | 45 ++++++++-- .../repositories/SharedItemsRepository.kt | 5 +- app/src/main/res/layout/dialog_poll_main.xml | 2 - 12 files changed, 139 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt rename app/src/main/java/com/nextcloud/talk/polls/repositories/model/{PollDetails.kt => PollDetailsResponse.kt} (74%) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 5bcd9ed83..c79c8dc8b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -117,11 +117,13 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH binding.messagePollTitle.text = pollName // TODO: how to get room token here? - val roomToken = "???????????????????????????" + // val roomToken = "???????????????????????????" + val roomToken = "i7ht5k9n" binding.bubble.setOnClickListener { val pollVoteDialog = PollMainDialogFragment.newInstance( - message.activeUser!!, roomToken, pollId, + roomToken, + pollId, pollName ) pollVoteDialog.show( diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt index ddad6a7ad..1646618d7 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt @@ -1,7 +1,5 @@ package com.nextcloud.talk.polls.model -import com.nextcloud.talk.polls.repositories.model.PollDetails - data class Poll( val id: String, val question: String?, @@ -15,6 +13,5 @@ data class Poll( val maxVotes: Int, val votedSelf: List?, val numVoters: Int, - // TODO PollDetails needs own model class val details: List? ) diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt b/app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt new file mode 100644 index 000000000..d4df031ae --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt @@ -0,0 +1,8 @@ +package com.nextcloud.talk.polls.model + +data class PollDetails( + val actorType: String?, + val actorId: String?, + val actorDisplayName: String?, + val optionId: Int +) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt index 45d1acfbe..37e3d3d4d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -5,5 +5,5 @@ import io.reactivex.Observable interface PollRepository { - fun getPoll(roomToken: String, pollId: String): Observable + fun getPoll(roomToken: String, pollId: String): Observable? } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 348f7d767..42142e436 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -23,30 +23,82 @@ package com.nextcloud.talk.polls.repositories import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.polls.model.Poll +import com.nextcloud.talk.polls.model.PollDetails +import com.nextcloud.talk.polls.repositories.model.PollDetailsResponse +import com.nextcloud.talk.polls.repositories.model.PollResponse +import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.database.user.CurrentUserProvider import io.reactivex.Observable -class PollRepositoryImpl(private val api: NcApi, private val currentUserProvider: CurrentUserProvider) : +class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvider: CurrentUserProvider) : PollRepository { override fun getPoll(roomToken: String, pollId: String): Observable { - // TODO actual api call - return Observable.just( - Poll( - id = "aaa", - question = "what if?", - options = listOf("yes", "no", "maybe", "I don't know"), - votes = listOf(0, 0, 0, 0), - actorType = "", - actorId = "", - actorDisplayName = "", - status = 0, - resultMode = 0, - maxVotes = 1, - votedSelf = listOf(0, 0, 0, 0), - numVoters = 0, - details = emptyList() - ) + + val credentials = ApiUtils.getCredentials( + currentUserProvider.currentUser?.username, + currentUserProvider.currentUser?.token ) + + return ncApi.getPoll( + credentials, + ApiUtils.getUrlForPoll( + currentUserProvider.currentUser?.baseUrl, + roomToken, + pollId + ), + ).map { mapToPoll(it.ocs?.data!!) } + + // // // TODO actual api call + // return Observable.just( + // Poll( + // id = "aaa", + // question = "what if?", + // options = listOf("yes", "no", "maybe", "I don't know"), + // votes = listOf(0, 0, 0, 0), + // actorType = "", + // actorId = "", + // actorDisplayName = "", + // status = 0, + // resultMode = 0, + // maxVotes = 1, + // votedSelf = listOf(0, 0, 0, 0), + // numVoters = 0, + // details = emptyList() + // ) + // ) + } + + companion object { + + private fun mapToPoll(pollResponse: PollResponse): Poll { + val pollDetails = pollResponse.details?.map { it -> mapToPollDetails(it) } + + val poll = Poll( + pollResponse.id, + pollResponse.question, + pollResponse.options, + pollResponse.votes, + pollResponse.actorType, + pollResponse.actorId, + pollResponse.actorDisplayName, + pollResponse.status, + pollResponse.resultMode, + pollResponse.maxVotes, + pollResponse.votedSelf, + pollResponse.numVoters, + pollDetails, + ) + return poll + } + + private fun mapToPollDetails(pollDetailsResponse: PollDetailsResponse): PollDetails { + return PollDetails( + pollDetailsResponse.actorType, + pollDetailsResponse.actorId, + pollDetailsResponse.actorDisplayName, + pollDetailsResponse.optionId, + ) + } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt similarity index 74% rename from app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt rename to app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt index 73f8fd21e..b5051eac4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetails.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt @@ -7,20 +7,20 @@ import kotlinx.android.parcel.Parcelize @Parcelize @JsonObject -data class PollDetails( +data class PollDetailsResponse( @JsonField(name = ["actorType"]) var actorType: String? = null, @JsonField(name = ["actorId"]) - var actorId: String? = null, + var actorId: String, @JsonField(name = ["actorDisplayName"]) - var actorDisplayName: String? = null, + var actorDisplayName: String, @JsonField(name = ["optionId"]) - var optionId: Int? = 0, + var optionId: Int, -) : Parcelable { + ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null, null, 0) + constructor() : this(null, "", "", 0) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt index 7aabfe12a..0bd86c56f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt @@ -28,7 +28,7 @@ import kotlinx.android.parcel.Parcelize @JsonObject data class PollResponse( @JsonField(name = ["id"]) - var id: Int = 0, + var id: String, @JsonField(name = ["question"]) var question: String? = null, @@ -64,9 +64,9 @@ data class PollResponse( var numVoters: Int = 0, @JsonField(name = ["details"]) - var details: ArrayList? = null, + var details: ArrayList? = null, -) : Parcelable { + ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(0, null, null, null, null, null, null, 0, 0, 0, null) + constructor() : this("id", null, null, null, null, null, null, 0, 0, 0, null, 0, null) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index e2c6a9191..3997618c2 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -12,7 +12,6 @@ import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollMainBinding -import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.polls.viewmodels.PollViewModel import javax.inject.Inject @@ -78,7 +77,6 @@ class PollMainDialogFragment( companion object { @JvmStatic fun newInstance( - userEntity: UserEntity, roomTokenParam: String, pollId: String, name: String diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index fec941504..6e733172b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -22,6 +22,7 @@ package com.nextcloud.talk.polls.ui import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -74,6 +75,7 @@ class PollVoteFragment(private val parentViewModel: PollViewModel) : Fragment() .also { it.setOnClickListener { // todo + Log.d("bb", "click1") } } }?.forEach { @@ -83,6 +85,7 @@ class PollVoteFragment(private val parentViewModel: PollViewModel) : Fragment() } binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> // todo set selected in viewmodel + Log.d("bb", "click") } // todo observe viewmodel checked, set view checked with it // todo listen to button click, submit diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt index 4ed59ab42..751d96f5c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt @@ -1,10 +1,12 @@ package com.nextcloud.talk.polls.viewmodels +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.repositories.PollRepository +import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers @@ -44,17 +46,48 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) loadPoll() } + // private fun loadPoll() { + // disposable = repository.getPoll(roomToken, pollId) + // ?.subscribeOn(Schedulers.io()) + // ?.observeOn(AndroidSchedulers.mainThread()) + // ?.subscribe { poll -> + // _viewState.value = PollOpenState(poll) + // } + // } + private fun loadPoll() { - disposable = repository.getPoll(roomToken, pollId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { poll -> - _viewState.value = PollOpenState(poll) - } + repository.getPoll(roomToken, pollId) + ?.doOnSubscribe { disposable = it } + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(PollObserver()) } override fun onCleared() { super.onCleared() disposable?.dispose() } + + inner class PollObserver : Observer { + + lateinit var poll: Poll + + override fun onSubscribe(d: Disposable) = Unit + + override fun onNext(response: Poll) { + poll = response + } + + override fun onError(e: Throwable) { + Log.d(TAG, "An error occurred: $e") + } + + override fun onComplete() { + _viewState.value = PollOpenState(poll) + } + } + + companion object { + private val TAG = PollViewModel::class.java.simpleName + } } diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt index a668d672d..62f69beee 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt @@ -28,7 +28,10 @@ import io.reactivex.Observable interface SharedItemsRepository { - fun media(parameters: Parameters, type: SharedItemType): Observable? + fun media( + parameters: Parameters, + type: SharedItemType + ): Observable? fun media( parameters: Parameters, diff --git a/app/src/main/res/layout/dialog_poll_main.xml b/app/src/main/res/layout/dialog_poll_main.xml index f4c5e2b62..ec88db28f 100644 --- a/app/src/main/res/layout/dialog_poll_main.xml +++ b/app/src/main/res/layout/dialog_poll_main.xml @@ -46,7 +46,6 @@ app:layout_constraintTop_toTopOf="@+id/message_poll_icon" tools:text="This is the poll title?" /> - - From ae80a4e2ad40fa69adc60a7db154eb736efbcf06 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 8 Jun 2022 18:05:29 +0200 Subject: [PATCH 07/92] pass roomToken to MessageViewHolders wrap roomToken and ProfileBottomSheet in data class MessagePayload Signed-off-by: Marcel Hibbe --- .../messages/IncomingLocationMessageViewHolder.kt | 3 +-- .../messages/IncomingPollMessageViewHolder.kt | 7 ++----- .../messages/IncomingVoiceMessageViewHolder.kt | 3 +-- .../MagicIncomingTextMessageViewHolder.kt | 4 +--- .../messages/MagicPreviewMessageViewHolder.java | 6 +++--- .../talk/adapters/messages/MessagePayload.kt | 8 ++++++++ .../nextcloud/talk/controllers/ChatController.kt | 15 +++++++++------ 7 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt 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 43d163878..2e9e723d4 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 @@ -50,7 +50,6 @@ 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.UriUtils @@ -117,7 +116,7 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : Mess if (!TextUtils.isEmpty(author)) { binding.messageAuthor.text = author binding.messageUserAvatar.setOnClickListener { - (payload as? ProfileBottomSheet)?.showFor(message.actorId!!, itemView.context) + (payload as? MessagePayload)?.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/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index c79c8dc8b..b1669bce3 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -41,7 +41,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA import com.nextcloud.talk.databinding.ItemCustomIncomingPollMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.polls.ui.PollMainDialogFragment -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 @@ -116,9 +115,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH if (pollId != null && pollName != null) { binding.messagePollTitle.text = pollName - // TODO: how to get room token here? - // val roomToken = "???????????????????????????" - val roomToken = "i7ht5k9n" + val roomToken = (payload as? MessagePayload)!!.roomToken binding.bubble.setOnClickListener { val pollVoteDialog = PollMainDialogFragment.newInstance( @@ -151,7 +148,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH if (!TextUtils.isEmpty(author)) { binding.messageAuthor.text = author binding.messageUserAvatar.setOnClickListener { - (payload as? ProfileBottomSheet)?.showFor(message.actorId!!, itemView.context) + (payload as? MessagePayload)?.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/IncomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt index a7e904da4..e06604763 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 @@ -48,7 +48,6 @@ 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 @@ -210,7 +209,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message if (!TextUtils.isEmpty(author)) { binding.messageAuthor.text = author binding.messageUserAvatar.setOnClickListener { - (payload as? ProfileBottomSheet)?.showFor(message.actorId!!, itemView.context) + (payload as? MessagePayload)?.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 d098ba697..95152a920 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 @@ -47,14 +47,12 @@ 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 import com.nextcloud.talk.utils.TextMatchers import com.nextcloud.talk.utils.preferences.AppPreferences import com.stfalcon.chatkit.messages.MessageHolders -import java.util.HashMap import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -136,7 +134,7 @@ class MagicIncomingTextMessageViewHolder(itemView: View, payload: Any) : Message if (!TextUtils.isEmpty(message.actorDisplayName)) { binding.messageAuthor.text = message.actorDisplayName binding.messageUserAvatar.setOnClickListener { - (payload as? ProfileBottomSheet)?.showFor(message.actorId!!, itemView.context) + (payload as? MessagePayload)?.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 0732e3341..48e33915e 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 @@ -49,7 +49,6 @@ import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation; import com.nextcloud.talk.data.user.model.User; import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding; import com.nextcloud.talk.models.json.chat.ChatMessage; -import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DrawableUtils; import com.nextcloud.talk.utils.FileViewerUtils; @@ -125,8 +124,9 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom } else { userAvatar.setVisibility(View.VISIBLE); userAvatar.setOnClickListener(v -> { - if (payload instanceof ProfileBottomSheet) { - ((ProfileBottomSheet) payload).showFor(message.getActorId(), v.getContext()); + if (payload instanceof MessagePayload) { + ((MessagePayload) payload).getProfileBottomSheet().showFor(message.getActorId(), + v.getContext()); } }); diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt new file mode 100644 index 000000000..59be4b481 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt @@ -0,0 +1,8 @@ +package com.nextcloud.talk.adapters.messages + +import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet + +data class MessagePayload( + val roomToken: String, + val profileBottomSheet: ProfileBottomSheet +) 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 dbe202e39..2b9a698b6 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -111,6 +111,7 @@ import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder +import com.nextcloud.talk.adapters.messages.MessagePayload import com.nextcloud.talk.adapters.messages.OutcomingLocationMessageViewHolder import com.nextcloud.talk.adapters.messages.OutcomingPollMessageViewHolder import com.nextcloud.talk.adapters.messages.OutcomingPreviewMessageViewHolder @@ -485,10 +486,12 @@ class ChatController(args: Bundle) : val messageHolders = MessageHolders() val profileBottomSheet = ProfileBottomSheet(ncApi!!, conversationUser!!, router) + val payload = MessagePayload(roomToken!!, profileBottomSheet) + messageHolders.setIncomingTextConfig( MagicIncomingTextMessageViewHolder::class.java, R.layout.item_custom_incoming_text_message, - profileBottomSheet + payload ) messageHolders.setOutcomingTextConfig( MagicOutcomingTextMessageViewHolder::class.java, @@ -498,7 +501,7 @@ class ChatController(args: Bundle) : messageHolders.setIncomingImageConfig( IncomingPreviewMessageViewHolder::class.java, R.layout.item_custom_incoming_preview_message, - profileBottomSheet + payload ) messageHolders.setOutcomingImageConfig( @@ -527,7 +530,7 @@ class ChatController(args: Bundle) : messageHolders.registerContentType( CONTENT_TYPE_LOCATION, IncomingLocationMessageViewHolder::class.java, - profileBottomSheet, + payload, R.layout.item_custom_incoming_location_message, OutcomingLocationMessageViewHolder::class.java, null, @@ -538,7 +541,7 @@ class ChatController(args: Bundle) : messageHolders.registerContentType( CONTENT_TYPE_VOICE_MESSAGE, IncomingVoiceMessageViewHolder::class.java, - profileBottomSheet, + payload, R.layout.item_custom_incoming_voice_message, OutcomingVoiceMessageViewHolder::class.java, null, @@ -549,10 +552,10 @@ class ChatController(args: Bundle) : messageHolders.registerContentType( CONTENT_TYPE_POLL, IncomingPollMessageViewHolder::class.java, - profileBottomSheet, + payload, R.layout.item_custom_incoming_poll_message, OutcomingPollMessageViewHolder::class.java, - null, + payload, R.layout.item_custom_outcoming_poll_message, this ) From 81666829b570333efcdcb3ee673b15d095fb3a35 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 8 Jun 2022 21:37:46 +0200 Subject: [PATCH 08/92] fetch poll to set subtitle in message Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 48 +++++++++++++++---- .../item_custom_incoming_poll_message.xml | 9 ++-- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index b1669bce3..8198748a0 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Build import android.text.TextUtils +import android.util.Log import android.view.View import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat @@ -40,11 +41,16 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.databinding.ItemCustomIncomingPollMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.polls.repositories.model.PollOverall import com.nextcloud.talk.polls.ui.PollMainDialogFragment import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.preferences.AppPreferences import com.stfalcon.chatkit.messages.MessageHolders +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -129,17 +135,39 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH ) } - // TODO get poll from api - // wait for https://github.com/nextcloud/spreed/pull/7306#issuecomment-1145819317 + val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) + ncApi!!.getPoll( + credentials, + ApiUtils.getUrlForPoll( + message.activeUser?.baseUrl, + roomToken, + pollId + ) + ).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - // val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) - // ncApi!!.getPoll( - // credentials, - // ApiUtils.getUrlForPoll( - // message.activeUser?.baseUrl, - // ??????? - // ) - // ) + override fun onNext(pollOverall: PollOverall) { + if (pollOverall.ocs!!.data!!.status == 0) { + binding.messagePollSubtitle.text = + context?.resources?.getString(R.string.message_poll_tap_to_vote) + } else { + binding.messagePollSubtitle.text = + context?.resources?.getString(R.string.message_poll_tap_see_results) + } + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error while fetching poll", e) + } + + override fun onComplete() { + // unused atm + } + }) } } diff --git a/app/src/main/res/layout/item_custom_incoming_poll_message.xml b/app/src/main/res/layout/item_custom_incoming_poll_message.xml index 7f198bd15..e069bf7c7 100644 --- a/app/src/main/res/layout/item_custom_incoming_poll_message.xml +++ b/app/src/main/res/layout/item_custom_incoming_poll_message.xml @@ -65,8 +65,8 @@ + android:gravity="center_vertical" + android:orientation="horizontal"> + app:tint="@color/high_emphasis_menu_icon" /> + android:text="@string/message_poll_tap_to_vote" /> Date: Mon, 13 Jun 2022 14:53:48 +0200 Subject: [PATCH 09/92] wip: vote on poll Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/api/NcApi.java | 3 +- .../com/nextcloud/talk/polls/model/Poll.kt | 9 ++- .../talk/polls/repositories/PollRepository.kt | 2 + .../polls/repositories/PollRepositoryImpl.kt | 23 ++++++-- .../talk/polls/ui/PollMainDialogFragment.kt | 6 +- .../talk/polls/ui/PollVoteFragment.kt | 44 +++++++++----- .../talk/polls/viewmodels/PollViewModel.kt | 19 +++--- .../polls/viewmodels/PollVoteViewModel.kt | 58 ++++++++++++++++++- app/src/main/res/layout/dialog_poll_vote.xml | 2 +- 9 files changed, 134 insertions(+), 32 deletions(-) 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 e6f0b8d97..1a0fbd8ed 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -538,7 +538,8 @@ public interface NcApi { @POST Observable votePoll(@Header("Authorization") String authorization, - @Url String url); + @Url String url, + @Query("optionIds[]") List optionIds); @DELETE Observable closePoll(@Header("Authorization") String authorization, diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt index 1646618d7..4433ace25 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt @@ -14,4 +14,11 @@ data class Poll( val votedSelf: List?, val numVoters: Int, val details: List? -) +) { + companion object { + const val STATUS_OPEN: Int = 0 + const val STATUS_CLOSED: Int = 1 + const val RESULT_MODE_PUBLIC: Int = 0 + const val RESULT_MODE_HIDDEN: Int = 1 + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt index 37e3d3d4d..cfc8b6d55 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -6,4 +6,6 @@ import io.reactivex.Observable interface PollRepository { fun getPoll(roomToken: String, pollId: String): Observable? + + fun vote(roomToken: String, pollId: String, option: Int): Observable? } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 42142e436..c02f7bd22 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -33,12 +33,12 @@ import io.reactivex.Observable class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvider: CurrentUserProvider) : PollRepository { - override fun getPoll(roomToken: String, pollId: String): Observable { + val credentials = ApiUtils.getCredentials( + currentUserProvider.currentUser?.username, + currentUserProvider.currentUser?.token + ) - val credentials = ApiUtils.getCredentials( - currentUserProvider.currentUser?.username, - currentUserProvider.currentUser?.token - ) + override fun getPoll(roomToken: String, pollId: String): Observable { return ncApi.getPoll( credentials, @@ -69,6 +69,19 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid // ) } + override fun vote(roomToken: String, pollId: String, option: Int): Observable? { + + return ncApi.votePoll( + credentials, + ApiUtils.getUrlForPoll( + currentUserProvider.currentUser?.baseUrl, + roomToken, + pollId + ), + arrayOf(option).asList() + ).map { mapToPoll(it.ocs?.data!!) } + } + companion object { private fun mapToPoll(pollResponse: PollResponse): Poll { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 3997618c2..8e90e2275 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -60,7 +60,11 @@ class PollMainDialogFragment( PollViewModel.InitialState -> {} is PollViewModel.PollClosedState -> TODO() is PollViewModel.PollOpenState -> { - val contentFragment = PollVoteFragment(viewModel) + val contentFragment = PollVoteFragment( + viewModel, + roomToken, + pollId + ) val transaction = childFragmentManager.beginTransaction() transaction.replace(binding.messagePollContentFragment.id, contentFragment) transaction.commit() diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 6e733172b..20a895eaf 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -37,7 +37,11 @@ import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollVoteFragment(private val parentViewModel: PollViewModel) : Fragment() { +class PollVoteFragment( + private val parentViewModel: PollViewModel, + private val roomToken: String, + private val pollId: String +) : Fragment() { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -70,29 +74,43 @@ class PollVoteFragment(private val parentViewModel: PollViewModel) : Fragment() val poll = state.poll binding.radioGroup.removeAllViews() poll.options?.map { option -> - RadioButton(context) - .apply { text = option } - .also { - it.setOnClickListener { - // todo - Log.d("bb", "click1") - } - } - }?.forEach { - binding.radioGroup.addView(it) + RadioButton(context).apply { text = option } + }?.forEachIndexed { index, radioButton -> + radioButton.id = index + binding.radioGroup.addView(radioButton) } } } + + viewModel.viewState.observe(viewLifecycleOwner) { state -> + when (state) { + PollVoteViewModel.InitialState -> {} + is PollVoteViewModel.PollVoteFailedState -> { + Log.d(TAG, "fail") + } + is PollVoteViewModel.PollVoteSuccessState -> { + parentViewModel.voted() + } + } + } + binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> - // todo set selected in viewmodel + // todo set selected in viewmodel. Log.d("bb", "click") } // todo observe viewmodel checked, set view checked with it - // todo listen to button click, submit + + binding.submitVote.setOnClickListener { + viewModel.vote(roomToken, pollId, binding.radioGroup.checkedRadioButtonId) + } } override fun onDestroyView() { super.onDestroyView() _binding = null } + + companion object { + private val TAG = PollVoteFragment::class.java.simpleName + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt index 751d96f5c..e1d0f65ae 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt @@ -31,6 +31,7 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) sealed interface ViewState object InitialState : ViewState open class PollOpenState(val poll: Poll) : ViewState + open class PollVotedState(val poll: Poll) : ViewState open class PollClosedState(val poll: Poll) : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) @@ -46,14 +47,9 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) loadPoll() } - // private fun loadPoll() { - // disposable = repository.getPoll(roomToken, pollId) - // ?.subscribeOn(Schedulers.io()) - // ?.observeOn(AndroidSchedulers.mainThread()) - // ?.subscribe { poll -> - // _viewState.value = PollOpenState(poll) - // } - // } + fun voted() { + loadPoll() // TODO load other view + } private fun loadPoll() { repository.getPoll(roomToken, pollId) @@ -83,7 +79,12 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) } override fun onComplete() { - _viewState.value = PollOpenState(poll) + // TODO check attributes and decide if poll is open/closed/selfvoted... + + when (poll.status) { + Poll.STATUS_OPEN -> _viewState.value = PollOpenState(poll) + Poll.STATUS_CLOSED -> _viewState.value = PollClosedState(poll) + } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 40bb6594d..7de9f7243 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -21,12 +21,31 @@ package com.nextcloud.talk.polls.viewmodels +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.nextcloud.talk.polls.model.Poll +import com.nextcloud.talk.polls.repositories.PollRepository +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class PollVoteViewModel @Inject constructor() : ViewModel() { +class PollVoteViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { + + sealed interface ViewState + object InitialState : ViewState + open class PollVoteSuccessState(val poll: Poll) : ViewState + open class PollVoteFailedState() : ViewState + + private val _viewState: MutableLiveData = MutableLiveData(InitialState) + val viewState: LiveData + get() = _viewState + + private var disposable: Disposable? = null + private val _selectedOptions: MutableLiveData> = MutableLiveData(emptyList()) val selectedOptions: LiveData> get() = _selectedOptions @@ -34,4 +53,41 @@ class PollVoteViewModel @Inject constructor() : ViewModel() { fun selectOption(option: String) { _selectedOptions.value = listOf(option) } + + fun vote(roomToken: String, pollId: String, option: Int) { + repository.vote(roomToken, pollId, option) + ?.doOnSubscribe { disposable = it } + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(PollObserver()) + } + + override fun onCleared() { + super.onCleared() + disposable?.dispose() + } + + inner class PollObserver : Observer { + + lateinit var poll: Poll + + override fun onSubscribe(d: Disposable) = Unit + + override fun onNext(response: Poll) { + poll = response + } + + override fun onError(e: Throwable) { + Log.d(TAG, "An error occurred: $e") + _viewState.value = PollVoteFailedState() + } + + override fun onComplete() { + _viewState.value = PollVoteSuccessState(poll) + } + } + + companion object { + private val TAG = PollVoteViewModel::class.java.simpleName + } } diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 094d756ce..102353541 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -33,7 +33,7 @@ Date: Tue, 14 Jun 2022 11:32:19 +0200 Subject: [PATCH 10/92] wip: view poll results Signed-off-by: Marcel Hibbe --- .../talk/dagger/modules/ViewModelModule.kt | 6 + .../talk/polls/adapters/PollResultItem.kt | 8 + .../adapters/PollResultItemClickListener.kt | 5 + .../polls/adapters/PollResultViewHolder.kt | 18 ++ .../talk/polls/adapters/PollResultsAdapter.kt | 25 +++ .../com/nextcloud/talk/polls/model/Poll.kt | 2 +- .../polls/repositories/PollRepositoryImpl.kt | 1 - .../polls/repositories/model/PollResponse.kt | 2 +- .../talk/polls/ui/PollMainDialogFragment.kt | 48 ++++-- .../talk/polls/ui/PollResultsFragment.kt | 159 ++++++++++++++++++ .../talk/polls/ui/PollVoteFragment.kt | 6 +- .../polls/viewmodels/PollResultsViewModel.kt | 50 ++++++ .../talk/polls/viewmodels/PollViewModel.kt | 22 ++- app/src/main/res/layout/dialog_poll_main.xml | 4 +- .../main/res/layout/dialog_poll_results.xml | 62 +++++++ app/src/main/res/layout/dialog_poll_vote.xml | 6 +- app/src/main/res/layout/poll_result_item.xml | 33 ++++ app/src/main/res/values/strings.xml | 3 + 18 files changed, 434 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt create mode 100644 app/src/main/res/layout/dialog_poll_results.xml create mode 100644 app/src/main/res/layout/poll_result_item.xml diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index cfccd7a23..338c0d340 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel import com.nextcloud.talk.messagesearch.MessageSearchViewModel +import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel import com.nextcloud.talk.polls.viewmodels.PollViewModel import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel @@ -73,6 +74,11 @@ abstract class ViewModelModule { @ViewModelKey(PollVoteViewModel::class) abstract fun pollVoteViewModel(viewModel: PollVoteViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(PollResultsViewModel::class) + abstract fun pollResultsViewModel(viewModel: PollResultsViewModel): ViewModel + @Binds @IntoMap @ViewModelKey(RemoteFileBrowserItemsViewModel::class) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt new file mode 100644 index 000000000..324253b6d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt @@ -0,0 +1,8 @@ +package com.nextcloud.talk.polls.adapters + +class PollResultItem( + val pollOption: String, + val pollPercent: Int + + // val voters.... +) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt new file mode 100644 index 000000000..0f73a37a2 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt @@ -0,0 +1,5 @@ +package com.nextcloud.talk.polls.adapters + +interface PollResultItemClickListener { + fun onClick(pollResultItem: PollResultItem) +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt new file mode 100644 index 000000000..ce928340d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -0,0 +1,18 @@ +package com.nextcloud.talk.polls.adapters + +import android.annotation.SuppressLint +import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.databinding.PollResultItemBinding + +class PollResultViewHolder( + private val binding: PollResultItemBinding +) : RecyclerView.ViewHolder(binding.root) { + + @SuppressLint("SetTextI18n") + fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { + binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } + binding.pollOptionText.text = pollResultItem.pollOption + binding.pollOptionPercentText.text = pollResultItem.pollPercent.toString() + "%" + binding.pollOptionBar.progress = pollResultItem.pollPercent + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt new file mode 100644 index 000000000..8c2471f94 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -0,0 +1,25 @@ +package com.nextcloud.talk.polls.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.databinding.PollResultItemBinding + +class PollResultsAdapter( + private val clickListener: PollResultItemClickListener +) : RecyclerView.Adapter() { + internal var list: MutableList = ArrayList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollResultViewHolder { + val itemBinding = PollResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return PollResultViewHolder(itemBinding) + } + + override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) { + holder.bind(list[position], clickListener) + } + + override fun getItemCount(): Int { + return list.size + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt index 4433ace25..f02829bfb 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt @@ -4,7 +4,7 @@ data class Poll( val id: String, val question: String?, val options: List?, - val votes: List?, + val votes: Map?, val actorType: String?, val actorId: String?, val actorDisplayName: String?, diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index c02f7bd22..9604baabc 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -49,7 +49,6 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid ), ).map { mapToPoll(it.ocs?.data!!) } - // // // TODO actual api call // return Observable.just( // Poll( // id = "aaa", diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt index 0bd86c56f..6215fb89b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt @@ -37,7 +37,7 @@ data class PollResponse( var options: ArrayList? = null, @JsonField(name = ["votes"]) - var votes: ArrayList? = null, + var votes: Map? = null, @JsonField(name = ["actorType"]) var actorType: String? = null, diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 8e90e2275..942e24b7d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -12,6 +12,7 @@ import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollMainBinding +import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollViewModel import javax.inject.Inject @@ -58,16 +59,21 @@ class PollMainDialogFragment( viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { PollViewModel.InitialState -> {} - is PollViewModel.PollClosedState -> TODO() - is PollViewModel.PollOpenState -> { - val contentFragment = PollVoteFragment( - viewModel, - roomToken, - pollId - ) - val transaction = childFragmentManager.beginTransaction() - transaction.replace(binding.messagePollContentFragment.id, contentFragment) - transaction.commit() + + is PollViewModel.PollVotedState -> { + if (state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { + showVoteFragment() + } else { + showResultsFragment() + } + } + + is PollViewModel.PollUnvotedState -> { + if (state.poll.status == Poll.STATUS_CLOSED) { + showResultsFragment() + } else { + showVoteFragment() + } } } } @@ -75,6 +81,28 @@ class PollMainDialogFragment( viewModel.initialize(roomToken, pollId) } + private fun showVoteFragment() { + val contentFragment = PollVoteFragment( + viewModel, + roomToken, + pollId + ) + val transaction = childFragmentManager.beginTransaction() + transaction.replace(binding.messagePollContentFragment.id, contentFragment) + transaction.commit() + } + + private fun showResultsFragment() { + val contentFragment = PollResultsFragment( + viewModel, + roomToken, + pollId + ) + val transaction = childFragmentManager.beginTransaction() + transaction.replace(binding.messagePollContentFragment.id, contentFragment) + transaction.commit() + } + /** * Fragment creator */ diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt new file mode 100644 index 000000000..16da389ec --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -0,0 +1,159 @@ +/* + * Nextcloud Talk application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * 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 . + */ + +package com.nextcloud.talk.polls.ui + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import autodagger.AutoInjector +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.databinding.DialogPollResultsBinding +import com.nextcloud.talk.polls.adapters.PollResultItem +import com.nextcloud.talk.polls.adapters.PollResultItemClickListener +import com.nextcloud.talk.polls.adapters.PollResultsAdapter +import com.nextcloud.talk.polls.model.Poll +import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel +import com.nextcloud.talk.polls.viewmodels.PollViewModel +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class PollResultsFragment( + private val parentViewModel: PollViewModel, + private val roomToken: String, + private val pollId: String +) : Fragment(), PollResultItemClickListener { + + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + lateinit var viewModel: PollResultsViewModel + + var _binding: DialogPollResultsBinding? = null + val binding: DialogPollResultsBinding + get() = _binding!! + + private var adapter: PollResultsAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + viewModel = ViewModelProvider(this, viewModelFactory)[PollResultsViewModel::class.java] + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = DialogPollResultsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + adapter = PollResultsAdapter(this) + _binding?.pollResultsList?.adapter = adapter + _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) + + parentViewModel.viewState.observe(viewLifecycleOwner) { state -> + if (state is PollViewModel.PollVotedState && + state.poll.resultMode == Poll.RESULT_MODE_PUBLIC + ) { + + initPollResults(state.poll) + initAmountVotersInfo(state) + initEditButton(state) + } else if (state is PollViewModel.PollUnvotedState && + state.poll.status == Poll.STATUS_CLOSED + ) { + Log.d(TAG, "show results also if self never voted") + } + + } + } + + private fun initPollResults(poll: Poll) { + if (poll.details != null) { + val votersAmount = poll.details.size + val oneVoteInPercent = 100 / votersAmount // TODO: poll.numVoters when fixed on api + + poll.options?.forEachIndexed { index, option -> + val votersForThisOption = poll.details.filter { it.optionId == index }.size + val optionsPercent = oneVoteInPercent * votersForThisOption + + val pollResultItem = PollResultItem(option, optionsPercent) // TODO add participants to PollResultItem + adapter?.list?.add(pollResultItem) + } + } else if (poll.votes != null) { + val votersAmount = poll.numVoters + val oneVoteInPercent = 100 / votersAmount + + poll.options?.forEachIndexed { index, option -> + val votersForThisOption = poll.votes?.filter { it.value == index }?.size!! + val optionsPercent = oneVoteInPercent * votersForThisOption + + val pollResultItem = PollResultItem(option, optionsPercent) + adapter?.list?.add(pollResultItem) + } + } else { + Log.e(TAG, "failed to get data to show poll results") + } + } + + private fun initAmountVotersInfo(state: PollViewModel.PollVotedState) { + _binding?.pollAmountVoters?.text = String.format( + resources.getString(R.string.polls_amount_voters), + state.poll.numVoters + ) + } + + private fun initEditButton(state: PollViewModel.PollVotedState) { + if (state.poll.status == Poll.STATUS_OPEN && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC) { + _binding?.editVoteButton?.visibility = View.VISIBLE + _binding?.editVoteButton?.setOnClickListener { + parentViewModel.edit() + } + } else { + _binding?.editVoteButton?.visibility = View.GONE + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + private val TAG = PollResultsFragment::class.java.simpleName + } + + override fun onClick(pollResultItem: PollResultItem) { + Log.d(TAG, "click..") + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 20a895eaf..c908053d8 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -32,6 +32,7 @@ import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollVoteBinding +import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollViewModel import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import javax.inject.Inject @@ -70,7 +71,7 @@ class PollVoteFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) parentViewModel.viewState.observe(viewLifecycleOwner) { state -> - if (state is PollViewModel.PollOpenState) { + if (state is PollViewModel.PollUnvotedState) { val poll = state.poll binding.radioGroup.removeAllViews() poll.options?.map { option -> @@ -79,6 +80,9 @@ class PollVoteFragment( radioButton.id = index binding.radioGroup.addView(radioButton) } + } else if (state is PollViewModel.PollVotedState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { + Log.d(TAG, "show vote screen also for resultMode hidden poll when already voted") + // TODO: other text for submit button } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt new file mode 100644 index 000000000..63bf30339 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -0,0 +1,50 @@ +/* + * Nextcloud Talk application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * 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 . + */ + +package com.nextcloud.talk.polls.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.nextcloud.talk.polls.repositories.PollRepository +import io.reactivex.disposables.Disposable +import javax.inject.Inject + +class PollResultsViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { + + sealed interface ViewState + object InitialState : ViewState + + private val _viewState: MutableLiveData = MutableLiveData(InitialState) + val viewState: LiveData + get() = _viewState + + private var disposable: Disposable? = null + + override fun onCleared() { + super.onCleared() + disposable?.dispose() + } + + companion object { + private val TAG = PollResultsViewModel::class.java.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt index e1d0f65ae..6bbb67b42 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt @@ -28,11 +28,12 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) private lateinit var roomToken: String private lateinit var pollId: String + private var editPoll: Boolean = false + sealed interface ViewState object InitialState : ViewState - open class PollOpenState(val poll: Poll) : ViewState + open class PollUnvotedState(val poll: Poll) : ViewState open class PollVotedState(val poll: Poll) : ViewState - open class PollClosedState(val poll: Poll) : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) val viewState: LiveData @@ -51,6 +52,11 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) loadPoll() // TODO load other view } + fun edit() { + editPoll = true + loadPoll() + } + private fun loadPoll() { repository.getPoll(roomToken, pollId) ?.doOnSubscribe { disposable = it } @@ -79,11 +85,13 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) } override fun onComplete() { - // TODO check attributes and decide if poll is open/closed/selfvoted... - - when (poll.status) { - Poll.STATUS_OPEN -> _viewState.value = PollOpenState(poll) - Poll.STATUS_CLOSED -> _viewState.value = PollClosedState(poll) + if (editPoll) { + _viewState.value = PollUnvotedState(poll) + editPoll = false + } else if (poll.votedSelf.isNullOrEmpty()) { + _viewState.value = PollUnvotedState(poll) + } else { + _viewState.value = PollVotedState(poll) } } } diff --git a/app/src/main/res/layout/dialog_poll_main.xml b/app/src/main/res/layout/dialog_poll_main.xml index ec88db28f..c03afd25d 100644 --- a/app/src/main/res/layout/dialog_poll_main.xml +++ b/app/src/main/res/layout/dialog_poll_main.xml @@ -38,9 +38,8 @@ diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml new file mode 100644 index 000000000..77a5d7430 --- /dev/null +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 102353541..060e9ea1c 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -28,9 +28,9 @@ android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent" android:layout_width="wrap_content" - app:layout_constraintTop_toTopOf="parent"> - - + app:layout_constraintTop_toTopOf="parent" + tools:layout_height="400dp" + tools:layout_width="match_parent"> + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 131b6bb5f..b0299a8f5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -534,6 +534,9 @@ Start typing to search … No search results + + Poll results - %1$s votes + Attachments All From d31a9b1ade56fc6c0543d559d7c857ed23573b2d Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 15 Jun 2022 17:22:52 +0200 Subject: [PATCH 11/92] fix to send votes Signed-off-by: Marcel Hibbe --- app/src/main/java/com/nextcloud/talk/api/NcApi.java | 3 ++- .../java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt | 2 +- app/src/main/res/layout/dialog_poll_vote.xml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) 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 1a0fbd8ed..8b4689b99 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -536,10 +536,11 @@ public interface NcApi { Observable createPoll(@Header("Authorization") String authorization, @Url String url); + @FormUrlEncoded @POST Observable votePoll(@Header("Authorization") String authorization, @Url String url, - @Query("optionIds[]") List optionIds); + @Field("optionIds[]") List optionIds); @DELETE Observable closePoll(@Header("Authorization") String authorization, diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 16da389ec..e17d59893 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -115,7 +115,7 @@ class PollResultsFragment( val oneVoteInPercent = 100 / votersAmount poll.options?.forEachIndexed { index, option -> - val votersForThisOption = poll.votes?.filter { it.value == index }?.size!! + val votersForThisOption = poll.votes?.filter { it.key.toInt() == index }?.size!! val optionsPercent = oneVoteInPercent * votersForThisOption val pollResultItem = PollResultItem(option, optionsPercent) diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 060e9ea1c..1e71b0244 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -30,7 +30,7 @@ android:layout_width="wrap_content" app:layout_constraintTop_toTopOf="parent" tools:layout_height="400dp" - tools:layout_width="match_parent"> + tools:layout_width="match_parent" /> Date: Wed, 15 Jun 2022 19:14:41 +0200 Subject: [PATCH 12/92] fix to show vote results if only "poll.votes" are available Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index e17d59893..6613c8f14 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -115,7 +115,10 @@ class PollResultsFragment( val oneVoteInPercent = 100 / votersAmount poll.options?.forEachIndexed { index, option -> - val votersForThisOption = poll.votes?.filter { it.key.toInt() == index }?.size!! + var votersForThisOption = poll.votes.filter { it.key.toInt() == index }[index.toString()] + if (votersForThisOption == null) { + votersForThisOption = 0 + } val optionsPercent = oneVoteInPercent * votersForThisOption val pollResultItem = PollResultItem(option, optionsPercent) From af427f8300262a03a9057fd683d61765a4a7308c Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 16 Jun 2022 15:34:03 +0200 Subject: [PATCH 13/92] wip: create poll. show outgoing polls Signed-off-by: Marcel Hibbe --- .../OutcomingPollMessageViewHolder.kt | 89 +++++++++- .../java/com/nextcloud/talk/api/NcApi.java | 7 +- .../talk/controllers/ChatController.kt | 11 ++ .../talk/dagger/modules/ViewModelModule.kt | 12 +- .../polls/adapters/PollCreateOptionItem.kt | 5 + .../adapters/PollCreateOptionViewHolder.kt | 53 ++++++ .../adapters/PollCreateOptionsAdapter.kt | 27 +++ .../PollCreateOptionsItemClickListener.kt | 5 + .../talk/polls/repositories/PollRepository.kt | 8 + .../polls/repositories/PollRepositoryImpl.kt | 18 ++ .../talk/polls/ui/PollCreateDialogFragment.kt | 155 ++++++++++++++++++ .../talk/polls/ui/PollMainDialogFragment.kt | 12 +- .../talk/polls/ui/PollResultsFragment.kt | 20 +-- .../talk/polls/ui/PollVoteFragment.kt | 8 +- .../polls/viewmodels/PollCreateViewModel.kt | 84 ++++++++++ ...{PollViewModel.kt => PollMainViewModel.kt} | 4 +- .../talk/ui/dialog/AttachmentDialog.kt | 5 + .../res/drawable/ic_baseline_close_24.xml | 10 ++ app/src/main/res/layout/dialog_attachment.xml | 33 ++++ .../main/res/layout/dialog_poll_create.xml | 133 +++++++++++++++ .../item_custom_outcoming_poll_message.xml | 31 +++- .../res/layout/poll_create_options_item.xml | 27 +++ app/src/main/res/values/strings.xml | 3 + 23 files changed, 727 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt rename app/src/main/java/com/nextcloud/talk/polls/viewmodels/{PollViewModel.kt => PollMainViewModel.kt} (93%) create mode 100644 app/src/main/res/drawable/ic_baseline_close_24.xml create mode 100644 app/src/main/res/layout/dialog_poll_create.xml create mode 100644 app/src/main/res/layout/poll_create_options_item.xml diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index fdbb4f2a4..ccf339b64 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -24,27 +24,35 @@ package com.nextcloud.talk.adapters.messages import android.annotation.SuppressLint import android.content.Context import android.graphics.PorterDuff -import android.os.Handler +import android.util.Log import android.view.View import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.ViewCompat import autodagger.AutoInjector import coil.load import com.nextcloud.talk.R +import com.nextcloud.talk.activities.MainActivity +import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.databinding.ItemCustomOutcomingPollMessageBinding import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ReadStatus +import com.nextcloud.talk.polls.repositories.model.PollOverall +import com.nextcloud.talk.polls.ui.PollMainDialogFragment import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.preferences.AppPreferences import com.stfalcon.chatkit.messages.MessageHolders +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class OutcomingPollMessageViewHolder(outcomingView: View) : MessageHolders -.OutcomingTextMessageViewHolder(outcomingView) { +class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : MessageHolders +.OutcomingTextMessageViewHolder(outcomingView, payload) { private val binding: ItemCustomOutcomingPollMessageBinding = ItemCustomOutcomingPollMessageBinding.bind(itemView) @@ -57,9 +65,11 @@ class OutcomingPollMessageViewHolder(outcomingView: View) : MessageHolders @Inject var appPreferences: AppPreferences? = null - lateinit var message: ChatMessage + @Inject + @JvmField + var ncApi: NcApi? = null - lateinit var handler: Handler + lateinit var message: ChatMessage lateinit var reactionsInterface: ReactionsInterface @@ -98,6 +108,8 @@ class OutcomingPollMessageViewHolder(outcomingView: View) : MessageHolders binding.checkMark.setContentDescription(readStatusContentDescriptionString) + setPollPreview(message) + Reaction().showReactions(message, binding.reactions, binding.messageTime.context, true) binding.reactions.reactionsEmojiWrapper.setOnClickListener { reactionsInterface.onClickReactions(message) @@ -108,6 +120,73 @@ class OutcomingPollMessageViewHolder(outcomingView: View) : MessageHolders } } + private fun setPollPreview(message: ChatMessage) { + var pollId: String? = null + var pollName: String? = null + + if (message.messageParameters != null && message.messageParameters!!.size > 0) { + for (key in message.messageParameters!!.keys) { + val individualHashMap: Map = message.messageParameters!![key]!! + if (individualHashMap["type"] == "talk-poll") { + pollId = individualHashMap["id"] + pollName = individualHashMap["name"].toString() + } + } + } + + if (pollId != null && pollName != null) { + binding.messagePollTitle.text = pollName + + val roomToken = (payload as? MessagePayload)!!.roomToken + + binding.bubble.setOnClickListener { + val pollVoteDialog = PollMainDialogFragment.newInstance( + roomToken, + pollId, + pollName + ) + pollVoteDialog.show( + (binding.messagePollIcon.context as MainActivity).supportFragmentManager, + TAG + ) + } + + val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) + ncApi!!.getPoll( + credentials, + ApiUtils.getUrlForPoll( + message.activeUser?.baseUrl, + roomToken, + pollId + ) + ).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(pollOverall: PollOverall) { + if (pollOverall.ocs!!.data!!.status == 0) { + binding.messagePollSubtitle.text = + context?.resources?.getString(R.string.message_poll_tap_to_vote) + } else { + binding.messagePollSubtitle.text = + context?.resources?.getString(R.string.message_poll_tap_see_results) + } + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error while fetching poll", e) + } + + override fun onComplete() { + // unused atm + } + }) + } + } + private fun setParentMessageDataOnMessageItem(message: ChatMessage) { if (!message.isDeleted && message.parentMessage != null) { val parentChatMessage = message.parentMessage 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 8b4689b99..5a531f5c7 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -532,9 +532,14 @@ public interface NcApi { Observable getPoll(@Header("Authorization") String authorization, @Url String url); + @FormUrlEncoded @POST Observable createPoll(@Header("Authorization") String authorization, - @Url String url); + @Url String url, + @Query("question") String question, + @Field("options[]") List options, + @Query("resultMode") Integer resultMode, + @Query("maxVotes") Integer maxVotes); @FormUrlEncoded @POST 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 2b9a698b6..0927abf94 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -142,6 +142,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.mention.Mention +import com.nextcloud.talk.polls.ui.PollCreateDialogFragment import com.nextcloud.talk.presenters.MentionAutocompletePresenter import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity import com.nextcloud.talk.shareditems.activities.SharedItemsActivity @@ -3138,6 +3139,16 @@ class ChatController(args: Bundle) : } } + fun createPoll() { + val pollVoteDialog = PollCreateDialogFragment.newInstance( + roomToken!! + ) + pollVoteDialog.show( + (activity as MainActivity?)!!.supportFragmentManager, + TAG + ) + } + companion object { private const val TAG = "ChatController" private const val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 1 diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index 338c0d340..3d8ee7535 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -25,8 +25,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel import com.nextcloud.talk.messagesearch.MessageSearchViewModel +import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel +import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel -import com.nextcloud.talk.polls.viewmodels.PollViewModel import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel import dagger.Binds @@ -66,8 +67,8 @@ abstract class ViewModelModule { @Binds @IntoMap - @ViewModelKey(PollViewModel::class) - abstract fun pollViewModel(viewModel: PollViewModel): ViewModel + @ViewModelKey(PollMainViewModel::class) + abstract fun pollViewModel(viewModel: PollMainViewModel): ViewModel @Binds @IntoMap @@ -79,6 +80,11 @@ abstract class ViewModelModule { @ViewModelKey(PollResultsViewModel::class) abstract fun pollResultsViewModel(viewModel: PollResultsViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(PollCreateViewModel::class) + abstract fun pollCreateViewModel(viewModel: PollCreateViewModel): ViewModel + @Binds @IntoMap @ViewModelKey(RemoteFileBrowserItemsViewModel::class) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt new file mode 100644 index 000000000..ae70d60d4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt @@ -0,0 +1,5 @@ +package com.nextcloud.talk.polls.adapters + +class PollCreateOptionItem( + var pollOption: String +) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt new file mode 100644 index 000000000..c41d883c8 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -0,0 +1,53 @@ +package com.nextcloud.talk.polls.adapters + +import android.annotation.SuppressLint +import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding + +class PollCreateOptionViewHolder( + private val binding: PollCreateOptionsItemBinding +) : RecyclerView.ViewHolder(binding.root) { + + @SuppressLint("SetTextI18n") + fun bind( + pollCreateOptionItem: PollCreateOptionItem, + clickListener: PollCreateOptionsItemClickListener, + position: Int + ) { + // binding.root.setOnClickListener { } + + binding.pollOptionDelete.setOnClickListener { + clickListener.onDeleteClick(pollCreateOptionItem, position) + } + + // binding.pollOptionText.addTextChangedListener(object : TextWatcher { + // override fun afterTextChanged(s: Editable) { + // // unused atm + // } + // + // override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // // unused atm + // } + // + // override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { + // pollCreateOptionItem.pollOption = option.toString() + // } + // }) + } + + // fun onBind(item: SharedItem) { + // Log.d("","bbbb") + // } + // + // fun onLongClick(view: View?): Boolean { + // // moviesList.remove(getAdapterPosition()) + // // notifyItemRemoved(getAdapterPosition()) + // + // Log.d("", "dfdrg") + // return true + // } + // + // override fun onClick(v: View?) { + // Log.d("", "dfdrg") + // } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt new file mode 100644 index 000000000..e39e1c507 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt @@ -0,0 +1,27 @@ +package com.nextcloud.talk.polls.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding + +class PollCreateOptionsAdapter( + private val clickListener: PollCreateOptionsItemClickListener +) : RecyclerView.Adapter() { + + internal var list: MutableList = ArrayList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollCreateOptionViewHolder { + val itemBinding = PollCreateOptionsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + + return PollCreateOptionViewHolder(itemBinding) + } + + override fun onBindViewHolder(holder: PollCreateOptionViewHolder, position: Int) { + holder.bind(list[position], clickListener, position) + } + + override fun getItemCount(): Int { + return list.size + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt new file mode 100644 index 000000000..6c17d0f0b --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt @@ -0,0 +1,5 @@ +package com.nextcloud.talk.polls.adapters + +interface PollCreateOptionsItemClickListener { + fun onDeleteClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt index cfc8b6d55..4654e170f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -8,4 +8,12 @@ interface PollRepository { fun getPoll(roomToken: String, pollId: String): Observable? fun vote(roomToken: String, pollId: String, option: Int): Observable? + + fun createPoll( + roomToken: String, + question: String, + options: List, + resultMode: Int, + maxVotes: Int + ): Observable? } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 9604baabc..c10e862cb 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -38,6 +38,24 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid currentUserProvider.currentUser?.token ) + override fun createPoll( + roomToken: String, question: String, options: List, resultMode: Int, maxVotes: + Int + ): + Observable? { + return ncApi.createPoll( + credentials, + ApiUtils.getUrlForPoll( + currentUserProvider.currentUser?.baseUrl, + roomToken + ), + question, + options, + resultMode, + maxVotes + ).map { mapToPoll(it.ocs?.data!!) } + } + override fun getPoll(roomToken: String, pollId: String): Observable { return ncApi.getPoll( diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt new file mode 100644 index 000000000..8f4b06e72 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -0,0 +1,155 @@ +package com.nextcloud.talk.polls.ui + +import android.annotation.SuppressLint +import android.app.Dialog +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import autodagger.AutoInjector +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.databinding.DialogPollCreateBinding +import com.nextcloud.talk.polls.adapters.PollCreateOptionItem +import com.nextcloud.talk.polls.adapters.PollCreateOptionsAdapter +import com.nextcloud.talk.polls.adapters.PollCreateOptionsItemClickListener +import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class PollCreateDialogFragment( + private val roomToken: String +) : DialogFragment(), PollCreateOptionsItemClickListener { + + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + private lateinit var binding: DialogPollCreateBinding + private lateinit var viewModel: PollCreateViewModel + + private var adapter: PollCreateOptionsAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + + viewModel = ViewModelProvider(this, viewModelFactory)[PollCreateViewModel::class.java] + } + + @SuppressLint("InflateParams") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogPollCreateBinding.inflate(LayoutInflater.from(context)) + + val dialog = AlertDialog.Builder(requireContext()) + .setView(binding.root) + .create() + + return dialog + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + adapter = PollCreateOptionsAdapter(this) + binding?.pollCreateOptionsList?.adapter = adapter + binding?.pollCreateOptionsList?.layoutManager = LinearLayoutManager(context) + + viewModel.initialize(roomToken) + + for (i in 1..3) { + val item = PollCreateOptionItem("a") + adapter?.list?.add(item) + } + + binding.pollAddOption.setOnClickListener { + val item = PollCreateOptionItem("a") + adapter?.list?.add(item) + adapter?.notifyDataSetChanged() + } + + binding.pollDismiss.setOnClickListener { + dismiss() + } + + + + + binding.pollCreateQuestion.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(s: Editable) { + // unused atm + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } + + override fun onTextChanged(question: CharSequence, start: Int, before: Int, count: Int) { + viewModel.question = question.toString() + } + }) + + // binding.option1.addTextChangedListener(object : TextWatcher { + // override fun afterTextChanged(s: Editable) { + // // unused atm + // } + // + // override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // // unused atm + // } + // + // override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { + // viewModel.options = listOf(option.toString()) + // } + // }) + + binding.pollPrivatePollCheckbox.setOnClickListener { + viewModel.multipleAnswer = binding.pollMultipleAnswersCheckbox.isChecked + } + + binding.pollMultipleAnswersCheckbox.setOnClickListener { + viewModel.multipleAnswer = binding.pollMultipleAnswersCheckbox.isChecked + } + + binding.pollCreateButton.setOnClickListener { + viewModel.createPoll() + } + + viewModel.viewState.observe(viewLifecycleOwner) { state -> + when (state) { + PollCreateViewModel.InitialState -> {} + + is PollCreateViewModel.PollCreatedState -> { + dismiss() + } + } + } + + viewModel.initialize(roomToken) + } + + override fun onDeleteClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) { + adapter?.list?.remove(pollCreateOptionItem) + adapter?.notifyItemRemoved(position) + } + + /** + * Fragment creator + */ + companion object { + private val TAG = PollCreateDialogFragment::class.java.simpleName + + @JvmStatic + fun newInstance( + roomTokenParam: String + ): PollCreateDialogFragment = PollCreateDialogFragment(roomTokenParam) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 942e24b7d..f7bb368c5 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -13,7 +13,7 @@ import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollMainBinding import com.nextcloud.talk.polls.model.Poll -import com.nextcloud.talk.polls.viewmodels.PollViewModel +import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -27,13 +27,13 @@ class PollMainDialogFragment( lateinit var viewModelFactory: ViewModelProvider.Factory private lateinit var binding: DialogPollMainBinding - private lateinit var viewModel: PollViewModel + private lateinit var viewModel: PollMainViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - viewModel = ViewModelProvider(this, viewModelFactory)[PollViewModel::class.java] + viewModel = ViewModelProvider(this, viewModelFactory)[PollMainViewModel::class.java] } @SuppressLint("InflateParams") @@ -58,9 +58,9 @@ class PollMainDialogFragment( viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { - PollViewModel.InitialState -> {} + PollMainViewModel.InitialState -> {} - is PollViewModel.PollVotedState -> { + is PollMainViewModel.PollVotedState -> { if (state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { showVoteFragment() } else { @@ -68,7 +68,7 @@ class PollMainDialogFragment( } } - is PollViewModel.PollUnvotedState -> { + is PollMainViewModel.PollUnvotedState -> { if (state.poll.status == Poll.STATUS_CLOSED) { showResultsFragment() } else { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 6613c8f14..0f42523a5 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -37,13 +37,13 @@ import com.nextcloud.talk.polls.adapters.PollResultItem import com.nextcloud.talk.polls.adapters.PollResultItemClickListener import com.nextcloud.talk.polls.adapters.PollResultsAdapter import com.nextcloud.talk.polls.model.Poll +import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel -import com.nextcloud.talk.polls.viewmodels.PollViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollResultsFragment( - private val parentViewModel: PollViewModel, + private val parentViewModel: PollMainViewModel, private val roomToken: String, private val pollId: String ) : Fragment(), PollResultItemClickListener { @@ -82,14 +82,14 @@ class PollResultsFragment( _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) parentViewModel.viewState.observe(viewLifecycleOwner) { state -> - if (state is PollViewModel.PollVotedState && + if (state is PollMainViewModel.PollVotedState && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC ) { initPollResults(state.poll) initAmountVotersInfo(state) initEditButton(state) - } else if (state is PollViewModel.PollUnvotedState && + } else if (state is PollMainViewModel.PollUnvotedState && state.poll.status == Poll.STATUS_CLOSED ) { Log.d(TAG, "show results also if self never voted") @@ -129,14 +129,14 @@ class PollResultsFragment( } } - private fun initAmountVotersInfo(state: PollViewModel.PollVotedState) { + private fun initAmountVotersInfo(state: PollMainViewModel.PollVotedState) { _binding?.pollAmountVoters?.text = String.format( resources.getString(R.string.polls_amount_voters), state.poll.numVoters ) } - private fun initEditButton(state: PollViewModel.PollVotedState) { + private fun initEditButton(state: PollMainViewModel.PollVotedState) { if (state.poll.status == Poll.STATUS_OPEN && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC) { _binding?.editVoteButton?.visibility = View.VISIBLE _binding?.editVoteButton?.setOnClickListener { @@ -147,6 +147,10 @@ class PollResultsFragment( } } + override fun onClick(pollResultItem: PollResultItem) { + Log.d(TAG, "click..") + } + override fun onDestroyView() { super.onDestroyView() _binding = null @@ -155,8 +159,4 @@ class PollResultsFragment( companion object { private val TAG = PollResultsFragment::class.java.simpleName } - - override fun onClick(pollResultItem: PollResultItem) { - Log.d(TAG, "click..") - } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index c908053d8..0f74d7af6 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -33,13 +33,13 @@ import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollVoteBinding import com.nextcloud.talk.polls.model.Poll -import com.nextcloud.talk.polls.viewmodels.PollViewModel +import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollVoteFragment( - private val parentViewModel: PollViewModel, + private val parentViewModel: PollMainViewModel, private val roomToken: String, private val pollId: String ) : Fragment() { @@ -71,7 +71,7 @@ class PollVoteFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) parentViewModel.viewState.observe(viewLifecycleOwner) { state -> - if (state is PollViewModel.PollUnvotedState) { + if (state is PollMainViewModel.PollUnvotedState) { val poll = state.poll binding.radioGroup.removeAllViews() poll.options?.map { option -> @@ -80,7 +80,7 @@ class PollVoteFragment( radioButton.id = index binding.radioGroup.addView(radioButton) } - } else if (state is PollViewModel.PollVotedState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { + } else if (state is PollMainViewModel.PollVotedState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { Log.d(TAG, "show vote screen also for resultMode hidden poll when already voted") // TODO: other text for submit button } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt new file mode 100644 index 000000000..268f96ff1 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -0,0 +1,84 @@ +package com.nextcloud.talk.polls.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.nextcloud.talk.polls.model.Poll +import com.nextcloud.talk.polls.repositories.PollRepository +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +class PollCreateViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { + + private lateinit var roomToken: String + + lateinit var question: String + lateinit var options: List + var privatePoll: Boolean = false + var multipleAnswer: Boolean = false + + sealed interface ViewState + object InitialState : ViewState + open class PollCreatingState() : ViewState + open class PollCreatedState() : ViewState + open class PollCreationFailedState() : ViewState + + private val _viewState: MutableLiveData = MutableLiveData(InitialState) + val viewState: LiveData + get() = _viewState + + private var disposable: Disposable? = null + + fun initialize(roomToken: String) { + this.roomToken = roomToken + } + + override fun onCleared() { + super.onCleared() + disposable?.dispose() + } + + fun createPoll() { + var maxVotes = 1 + if (multipleAnswer) { + maxVotes = 0 + } + + var resultMode = 0 + if (privatePoll) { + resultMode = 1 + } + + repository.createPoll(roomToken, question, options, resultMode, maxVotes) + ?.doOnSubscribe { disposable = it } + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(PollObserver()) + } + + inner class PollObserver : Observer { + + lateinit var poll: Poll + + override fun onSubscribe(d: Disposable) = Unit + + override fun onNext(response: Poll) { + poll = response + } + + override fun onError(e: Throwable) { + _viewState.value = PollCreationFailedState() + } + + override fun onComplete() { + _viewState.value = PollCreatedState() + } + } + + companion object { + private val TAG = PollCreateViewModel::class.java.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt similarity index 93% rename from app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt rename to app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 6bbb67b42..28594beab 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -23,7 +23,7 @@ import javax.inject.Inject * InitialState --> PollClosedState * @enduml */ -class PollViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { +class PollMainViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { private lateinit var roomToken: String private lateinit var pollId: String @@ -97,6 +97,6 @@ class PollViewModel @Inject constructor(private val repository: PollRepository) } companion object { - private val TAG = PollViewModel::class.java.simpleName + private val TAG = PollMainViewModel::class.java.simpleName } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt index c760990ab..2da8b2d76 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt @@ -74,6 +74,11 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle dismiss() } + dialogAttachmentBinding.menuAttachPoll.setOnClickListener { + chatController.createPoll() + dismiss() + } + dialogAttachmentBinding.menuAttachFileFromCloud.setOnClickListener { chatController.showBrowserScreen() dismiss() diff --git a/app/src/main/res/drawable/ic_baseline_close_24.xml b/app/src/main/res/drawable/ic_baseline_close_24.xml new file mode 100644 index 000000000..1d6c00461 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_close_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/dialog_attachment.xml b/app/src/main/res/layout/dialog_attachment.xml index ed381203c..8280c8a90 100644 --- a/app/src/main/res/layout/dialog_attachment.xml +++ b/app/src/main/res/layout/dialog_attachment.xml @@ -39,6 +39,39 @@ android:textColor="@color/medium_emphasis_text" android:textSize="@dimen/bottom_sheet_text_size" /> + + + + + + + + + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2, + as published by the Free Software Foundation. + + 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 . +--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_custom_outcoming_poll_message.xml b/app/src/main/res/layout/item_custom_outcoming_poll_message.xml index 0a52ee34b..9bfbf2adc 100644 --- a/app/src/main/res/layout/item_custom_outcoming_poll_message.xml +++ b/app/src/main/res/layout/item_custom_outcoming_poll_message.xml @@ -47,11 +47,36 @@ + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + diff --git a/app/src/main/res/layout/poll_create_options_item.xml b/app/src/main/res/layout/poll_create_options_item.xml new file mode 100644 index 000000000..cfa9a0002 --- /dev/null +++ b/app/src/main/res/layout/poll_create_options_item.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b0299a8f5..cab13f522 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ No Skip Set + Dismiss Sorry, something went wrong! Submit @@ -404,6 +405,7 @@ Add to conversation Take photo + Create poll Share from %1$s Sorry, upload failed Choose files @@ -536,6 +538,7 @@ Poll results - %1$s votes + Add Option Attachments From b140cc8751d1546e4492ed73d980d17eec22d550 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 20 Jun 2022 10:48:53 +0200 Subject: [PATCH 14/92] wip: create poll using livedata Signed-off-by: Marcel Hibbe --- .../adapters/PollCreateOptionViewHolder.kt | 61 +++++++++---------- .../adapters/PollCreateOptionsAdapter.kt | 10 ++- .../PollCreateOptionsItemClickListener.kt | 2 +- .../talk/polls/ui/PollCreateDialogFragment.kt | 55 ++++++++--------- .../polls/viewmodels/PollCreateViewModel.kt | 51 +++++++++++++++- .../main/res/layout/dialog_poll_create.xml | 2 +- .../res/layout/poll_create_options_item.xml | 2 +- 7 files changed, 116 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index c41d883c8..a93f4d35e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -1,53 +1,52 @@ package com.nextcloud.talk.polls.adapters import android.annotation.SuppressLint +import android.text.Editable +import android.text.TextWatcher import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding +import com.nextcloud.talk.utils.EmojiTextInputEditText class PollCreateOptionViewHolder( private val binding: PollCreateOptionsItemBinding ) : RecyclerView.ViewHolder(binding.root) { + lateinit var optionText: EmojiTextInputEditText + var textListener: TextWatcher? = null + @SuppressLint("SetTextI18n") fun bind( pollCreateOptionItem: PollCreateOptionItem, clickListener: PollCreateOptionsItemClickListener, position: Int ) { - // binding.root.setOnClickListener { } - binding.pollOptionDelete.setOnClickListener { - clickListener.onDeleteClick(pollCreateOptionItem, position) + textListener?.let { + binding.pollOptionText.removeTextChangedListener(it) } - // binding.pollOptionText.addTextChangedListener(object : TextWatcher { - // override fun afterTextChanged(s: Editable) { - // // unused atm - // } - // - // override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // // unused atm - // } - // - // override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { - // pollCreateOptionItem.pollOption = option.toString() - // } - // }) + binding.pollOptionText.setText(pollCreateOptionItem.pollOption) + + binding.pollOptionDelete.setOnClickListener { + clickListener.onRemoveOptionsItemClick(pollCreateOptionItem, position) + } + + textListener = getTextWatcher(pollCreateOptionItem) + binding.pollOptionText.addTextChangedListener(textListener) } - // fun onBind(item: SharedItem) { - // Log.d("","bbbb") - // } - // - // fun onLongClick(view: View?): Boolean { - // // moviesList.remove(getAdapterPosition()) - // // notifyItemRemoved(getAdapterPosition()) - // - // Log.d("", "dfdrg") - // return true - // } - // - // override fun onClick(v: View?) { - // Log.d("", "dfdrg") - // } + private fun getTextWatcher(pollCreateOptionItem: PollCreateOptionItem) = + object : TextWatcher { + override fun afterTextChanged(s: Editable) { + // unused atm + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } + + override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { + pollCreateOptionItem.pollOption = option.toString() + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt index e39e1c507..4271f48d2 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt @@ -9,7 +9,7 @@ class PollCreateOptionsAdapter( private val clickListener: PollCreateOptionsItemClickListener ) : RecyclerView.Adapter() { - internal var list: MutableList = ArrayList() + internal var list: ArrayList = ArrayList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollCreateOptionViewHolder { val itemBinding = PollCreateOptionsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) @@ -18,10 +18,16 @@ class PollCreateOptionsAdapter( } override fun onBindViewHolder(holder: PollCreateOptionViewHolder, position: Int) { - holder.bind(list[position], clickListener, position) + val currentItem = list[position] + holder.bind(currentItem, clickListener, position) } override fun getItemCount(): Int { return list.size } + + fun updateOptionsList(optionsList: ArrayList) { + list = optionsList + notifyDataSetChanged() + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt index 6c17d0f0b..ec0169532 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt @@ -1,5 +1,5 @@ package com.nextcloud.talk.polls.adapters interface PollCreateOptionsItemClickListener { - fun onDeleteClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) + fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 8f4b06e72..9ed34f023 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -10,6 +10,7 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment +import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector @@ -39,6 +40,7 @@ class PollCreateDialogFragment( NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollCreateViewModel::class.java] + viewModel.options.observe(this, optionsObserver) } @SuppressLint("InflateParams") @@ -59,21 +61,19 @@ class PollCreateDialogFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + binding.pollCreateOptionsList.layoutManager = LinearLayoutManager(context) + adapter = PollCreateOptionsAdapter(this) - binding?.pollCreateOptionsList?.adapter = adapter - binding?.pollCreateOptionsList?.layoutManager = LinearLayoutManager(context) + binding.pollCreateOptionsList.adapter = adapter viewModel.initialize(roomToken) - for (i in 1..3) { - val item = PollCreateOptionItem("a") - adapter?.list?.add(item) - } + binding.pollAddOptionsItem.setOnClickListener { + viewModel.addOption() + // viewModel.options?.value?.let { it1 -> adapter?.notifyItemInserted(it1.size) } - binding.pollAddOption.setOnClickListener { - val item = PollCreateOptionItem("a") - adapter?.list?.add(item) - adapter?.notifyDataSetChanged() + // viewModel.options?.value?.let { it1 -> adapter?.notifyItemChanged(it1.size) } + // viewModel.options?.value?.let { it1 -> adapter?.notifyItemRangeInserted(it1.size, 1) } } binding.pollDismiss.setOnClickListener { @@ -93,25 +93,16 @@ class PollCreateDialogFragment( } override fun onTextChanged(question: CharSequence, start: Int, before: Int, count: Int) { + // TODO make question a livedata + // if(question != viewmodel.question.value) viewModel.setQuestion(question) viewModel.question = question.toString() } }) - // binding.option1.addTextChangedListener(object : TextWatcher { - // override fun afterTextChanged(s: Editable) { - // // unused atm - // } - // - // override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // // unused atm - // } - // - // override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { - // viewModel.options = listOf(option.toString()) - // } - // }) + // viewModel.question.observe { it -> binding.pollCreateQuestion.text = it } binding.pollPrivatePollCheckbox.setOnClickListener { + // FIXME viewModel.multipleAnswer = binding.pollMultipleAnswersCheckbox.isChecked } @@ -132,15 +123,23 @@ class PollCreateDialogFragment( } } } - - viewModel.initialize(roomToken) } - override fun onDeleteClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) { - adapter?.list?.remove(pollCreateOptionItem) - adapter?.notifyItemRemoved(position) + override fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) { + viewModel.removeOption(pollCreateOptionItem) + // adapter?.notifyItemRemoved(position) + + // adapter?.notifyItemChanged(position) + // adapter?.notifyItemRangeRemoved(position, 1) } + var optionsObserver: Observer> = + object : Observer> { + override fun onChanged(options: ArrayList) { + adapter?.updateOptionsList(options) + } + } + /** * Fragment creator */ diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index 268f96ff1..7b5c4ad2a 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -3,6 +3,7 @@ package com.nextcloud.talk.polls.viewmodels import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.nextcloud.talk.polls.adapters.PollCreateOptionItem import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.repositories.PollRepository import io.reactivex.Observer @@ -15,8 +16,22 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi private lateinit var roomToken: String - lateinit var question: String - lateinit var options: List + // TODO remove testing items + var defaultOptions: MutableList = mutableListOf().apply { + for (i in 1..3) { + val item = PollCreateOptionItem("item " + i) + this.add(item) + } + } + + private var _options: MutableLiveData> = + MutableLiveData>(defaultOptions) + val options: LiveData> + get() = _options + + var question: String = "" + + // lateinit var options: List var privatePoll: Boolean = false var multipleAnswer: Boolean = false @@ -41,6 +56,23 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi disposable?.dispose() } + private fun MutableLiveData.notifyObserver() { + this.value = this.value + } + + fun addOption() { + val item = PollCreateOptionItem("") + val currentOptions: ArrayList = _options.value ?: ArrayList() + currentOptions.add(item) + _options.value = currentOptions + } + + fun removeOption(item: PollCreateOptionItem) { + val currentOptions: ArrayList = _options.value ?: ArrayList() + currentOptions.remove(item) + _options.value = currentOptions + } + fun createPoll() { var maxVotes = 1 if (multipleAnswer) { @@ -52,13 +84,26 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi resultMode = 1 } - repository.createPoll(roomToken, question, options, resultMode, maxVotes) + val items = _options.value!! + repository.createPoll(roomToken, question, items.map { it.pollOption }, resultMode, maxVotes) ?.doOnSubscribe { disposable = it } ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(PollObserver()) } + private fun map( + list: ArrayList, + ): List { + val resultList: ArrayList? = ArrayList() + + list.forEach { + resultList?.add(it.pollOption) + } + + return resultList?.toList()!! + } + inner class PollObserver : Observer { lateinit var poll: Poll diff --git a/app/src/main/res/layout/dialog_poll_create.xml b/app/src/main/res/layout/dialog_poll_create.xml index 39ac6c715..50b3e77a0 100644 --- a/app/src/main/res/layout/dialog_poll_create.xml +++ b/app/src/main/res/layout/dialog_poll_create.xml @@ -68,7 +68,7 @@ - Date: Mon, 20 Jun 2022 21:42:49 +0200 Subject: [PATCH 15/92] wip: create poll using multiple livedata fields Signed-off-by: Marcel Hibbe --- .../adapters/PollCreateOptionViewHolder.kt | 4 +- .../adapters/PollCreateOptionsAdapter.kt | 2 +- ...er.kt => PollCreateOptionsItemListener.kt} | 3 +- .../talk/polls/ui/PollCreateDialogFragment.kt | 82 +++++++++++-------- .../polls/viewmodels/PollCreateViewModel.kt | 67 +++++++-------- 5 files changed, 87 insertions(+), 71 deletions(-) rename app/src/main/java/com/nextcloud/talk/polls/adapters/{PollCreateOptionsItemClickListener.kt => PollCreateOptionsItemListener.kt} (74%) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index a93f4d35e..f0b81e973 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -17,7 +17,7 @@ class PollCreateOptionViewHolder( @SuppressLint("SetTextI18n") fun bind( pollCreateOptionItem: PollCreateOptionItem, - clickListener: PollCreateOptionsItemClickListener, + itemsListener: PollCreateOptionsItemListener, position: Int ) { @@ -28,7 +28,7 @@ class PollCreateOptionViewHolder( binding.pollOptionText.setText(pollCreateOptionItem.pollOption) binding.pollOptionDelete.setOnClickListener { - clickListener.onRemoveOptionsItemClick(pollCreateOptionItem, position) + itemsListener.onRemoveOptionsItemClick(pollCreateOptionItem, position) } textListener = getTextWatcher(pollCreateOptionItem) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt index 4271f48d2..94adc523e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt @@ -6,7 +6,7 @@ import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding class PollCreateOptionsAdapter( - private val clickListener: PollCreateOptionsItemClickListener + private val clickListener: PollCreateOptionsItemListener ) : RecyclerView.Adapter() { internal var list: ArrayList = ArrayList() diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt similarity index 74% rename from app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt rename to app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt index ec0169532..751c95d4b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt @@ -1,5 +1,6 @@ package com.nextcloud.talk.polls.adapters -interface PollCreateOptionsItemClickListener { +interface PollCreateOptionsItemListener { + fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 9ed34f023..365506133 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -10,7 +10,6 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment -import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector @@ -18,14 +17,14 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollCreateBinding import com.nextcloud.talk.polls.adapters.PollCreateOptionItem import com.nextcloud.talk.polls.adapters.PollCreateOptionsAdapter -import com.nextcloud.talk.polls.adapters.PollCreateOptionsItemClickListener +import com.nextcloud.talk.polls.adapters.PollCreateOptionsItemListener import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollCreateDialogFragment( private val roomToken: String -) : DialogFragment(), PollCreateOptionsItemClickListener { +) : DialogFragment(), PollCreateOptionsItemListener { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -40,7 +39,6 @@ class PollCreateDialogFragment( NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollCreateViewModel::class.java] - viewModel.options.observe(this, optionsObserver) } @SuppressLint("InflateParams") @@ -61,6 +59,11 @@ class PollCreateDialogFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.options.observe(viewLifecycleOwner) { options -> adapter?.updateOptionsList(options) } + viewModel.question.observe(viewLifecycleOwner) { binding.pollCreateQuestion.setText(it) } + viewModel.privatePoll.observe(viewLifecycleOwner) { binding.pollPrivatePollCheckbox.isChecked = it } + viewModel.multipleAnswer.observe(viewLifecycleOwner) { binding.pollMultipleAnswersCheckbox.isChecked = it } + binding.pollCreateOptionsList.layoutManager = LinearLayoutManager(context) adapter = PollCreateOptionsAdapter(this) @@ -68,21 +71,19 @@ class PollCreateDialogFragment( viewModel.initialize(roomToken) + setupListeners() + setupStateObserver() + } + + private fun setupListeners() { binding.pollAddOptionsItem.setOnClickListener { viewModel.addOption() - // viewModel.options?.value?.let { it1 -> adapter?.notifyItemInserted(it1.size) } - - // viewModel.options?.value?.let { it1 -> adapter?.notifyItemChanged(it1.size) } - // viewModel.options?.value?.let { it1 -> adapter?.notifyItemRangeInserted(it1.size, 1) } } binding.pollDismiss.setOnClickListener { dismiss() } - - - binding.pollCreateQuestion.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable) { // unused atm @@ -93,53 +94,64 @@ class PollCreateDialogFragment( } override fun onTextChanged(question: CharSequence, start: Int, before: Int, count: Int) { - // TODO make question a livedata - // if(question != viewmodel.question.value) viewModel.setQuestion(question) - viewModel.question = question.toString() + if (question.toString() != viewModel.question.value) { + viewModel.setQuestion(question.toString()) + binding.pollCreateQuestion.setSelection(binding.pollCreateQuestion.length()) + } } }) - // viewModel.question.observe { it -> binding.pollCreateQuestion.text = it } - binding.pollPrivatePollCheckbox.setOnClickListener { - // FIXME - viewModel.multipleAnswer = binding.pollMultipleAnswersCheckbox.isChecked + viewModel.setPrivatePoll(binding.pollPrivatePollCheckbox.isChecked) } binding.pollMultipleAnswersCheckbox.setOnClickListener { - viewModel.multipleAnswer = binding.pollMultipleAnswersCheckbox.isChecked + viewModel.setMultipleAnswer(binding.pollMultipleAnswersCheckbox.isChecked) } binding.pollCreateButton.setOnClickListener { viewModel.createPoll() } + } + private fun setupStateObserver() { viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { - PollCreateViewModel.InitialState -> {} - - is PollCreateViewModel.PollCreatedState -> { - dismiss() - } + // PollCreateViewModel.InitialState -> showInitial() + is PollCreateViewModel.PollCreatedState -> dismiss() + is PollCreateViewModel.PollCreationFailedState -> dismiss() + is PollCreateViewModel.PollCreatingState -> updateDialog(state) } } + // viewModel.state.observe(this) { state -> + // when (state) { + // MessageSearchViewModel.InitialState -> showInitial() + // MessageSearchViewModel.EmptyState -> showEmpty() + // is MessageSearchViewModel.LoadedState -> showLoaded(state) + // MessageSearchViewModel.LoadingState -> showLoading() + // MessageSearchViewModel.ErrorState -> showError() + // is MessageSearchViewModel.FinishedState -> onFinish() + // } + // } + } + + private fun updateDialog(state: PollCreateViewModel.PollCreatingState) { + // binding.pollCreateQuestion.setText(state.question) + // + // adapter!!.updateOptionsList(state.options) + // + // binding.pollPrivatePollCheckbox.isChecked = state.privatePoll + // binding.pollMultipleAnswersCheckbox.isChecked = state.multipleAnswer + } + + private fun showInitial() { + binding.pollCreateButton.isEnabled = false } override fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) { viewModel.removeOption(pollCreateOptionItem) - // adapter?.notifyItemRemoved(position) - - // adapter?.notifyItemChanged(position) - // adapter?.notifyItemRangeRemoved(position, 1) } - var optionsObserver: Observer> = - object : Observer> { - override fun onChanged(options: ArrayList) { - adapter?.updateOptionsList(options) - } - } - /** * Fragment creator */ diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index 7b5c4ad2a..05f3fabe4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -16,24 +16,27 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi private lateinit var roomToken: String - // TODO remove testing items - var defaultOptions: MutableList = mutableListOf().apply { - for (i in 1..3) { - val item = PollCreateOptionItem("item " + i) - this.add(item) - } - } + // private var _options: MutableLiveData> = + // MutableLiveData>() + // val options: LiveData> + // get() = _options private var _options: MutableLiveData> = - MutableLiveData>(defaultOptions) + MutableLiveData>() val options: LiveData> get() = _options - var question: String = "" + private var _question: MutableLiveData = MutableLiveData() + val question: LiveData + get() = _question - // lateinit var options: List - var privatePoll: Boolean = false - var multipleAnswer: Boolean = false + private var _privatePoll: MutableLiveData = MutableLiveData() + var privatePoll: LiveData = _privatePoll + get() = _privatePoll + + private var _multipleAnswer: MutableLiveData = MutableLiveData() + var multipleAnswer: LiveData = _multipleAnswer + get() = _multipleAnswer sealed interface ViewState object InitialState : ViewState @@ -56,10 +59,6 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi disposable?.dispose() } - private fun MutableLiveData.notifyObserver() { - this.value = this.value - } - fun addOption() { val item = PollCreateOptionItem("") val currentOptions: ArrayList = _options.value ?: ArrayList() @@ -75,33 +74,37 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi fun createPoll() { var maxVotes = 1 - if (multipleAnswer) { + if (multipleAnswer.value == true) { maxVotes = 0 } var resultMode = 0 - if (privatePoll) { + if (privatePoll.value == true) { resultMode = 1 } - val items = _options.value!! - repository.createPoll(roomToken, question, items.map { it.pollOption }, resultMode, maxVotes) - ?.doOnSubscribe { disposable = it } - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(PollObserver()) + if (question.value?.isNotEmpty() == true && _options.value?.isNotEmpty() == true) { + repository.createPoll( + roomToken, question.value!!, _options.value!!.map { it.pollOption }, resultMode, + maxVotes + ) + ?.doOnSubscribe { disposable = it } + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(PollObserver()) + } } - private fun map( - list: ArrayList, - ): List { - val resultList: ArrayList? = ArrayList() + fun setQuestion(question: String) { + _question.value = question + } - list.forEach { - resultList?.add(it.pollOption) - } + fun setPrivatePoll(checked: Boolean) { + _privatePoll.value = checked + } - return resultList?.toList()!! + fun setMultipleAnswer(checked: Boolean) { + _multipleAnswer.value = checked } inner class PollObserver : Observer { From 8148bfaa6503567843925cdb155c635c9a88763e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 21 Jun 2022 14:22:41 +0200 Subject: [PATCH 16/92] add poll system message, klint fixes Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/models/json/chat/ChatMessage.kt | 3 ++- .../json/converters/EnumSystemMessageTypeConverter.kt | 3 +++ .../talk/polls/repositories/PollRepositoryImpl.kt | 9 ++++++--- .../com/nextcloud/talk/polls/ui/PollResultsFragment.kt | 3 +-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index faa0f1e50..312ad14a5 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt @@ -490,7 +490,8 @@ data class ChatMessage( CLEARED_CHAT, REACTION, REACTION_DELETED, - REACTION_REVOKED + REACTION_REVOKED, + POLL_VOTED } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt index caa75ec2b..3a8049a78 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt @@ -65,6 +65,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERAT import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.OBJECT_SHARED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_REMOVED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_SET +import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.POLL_VOTED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION_DELETED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION_REVOKED @@ -167,6 +168,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter REACTION "reaction_deleted" -> REACTION_DELETED "reaction_revoked" -> REACTION_REVOKED + "poll_voted" -> POLL_VOTED else -> DUMMY } } @@ -224,6 +226,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter return "reaction" REACTION_DELETED -> return "reaction_deleted" REACTION_REVOKED -> return "reaction_revoked" + POLL_VOTED -> return "poll_voted" else -> return "" } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index c10e862cb..e95843190 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -39,10 +39,13 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid ) override fun createPoll( - roomToken: String, question: String, options: List, resultMode: Int, maxVotes: + roomToken: String, + question: String, + options: List, + resultMode: Int, + maxVotes: Int - ): - Observable? { + ): Observable? { return ncApi.createPoll( credentials, ApiUtils.getUrlForPoll( diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 0f42523a5..4ed2c9404 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -94,14 +94,13 @@ class PollResultsFragment( ) { Log.d(TAG, "show results also if self never voted") } - } } private fun initPollResults(poll: Poll) { if (poll.details != null) { val votersAmount = poll.details.size - val oneVoteInPercent = 100 / votersAmount // TODO: poll.numVoters when fixed on api + val oneVoteInPercent = 100 / votersAmount // TODO: poll.numVoters when fixed on api poll.options?.forEachIndexed { index, option -> val votersForThisOption = poll.details.filter { it.optionId == index }.size From ea0b7d07cceed6dfadece2f48408573d45f9ef14 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 21 Jun 2022 16:52:17 +0200 Subject: [PATCH 17/92] poll creation: add logic to enable/disable buttons Signed-off-by: Marcel Hibbe --- .../adapters/PollCreateOptionViewHolder.kt | 9 ++- .../adapters/PollCreateOptionsItemListener.kt | 2 + .../talk/polls/ui/PollCreateDialogFragment.kt | 39 +++++----- .../polls/viewmodels/PollCreateViewModel.kt | 76 ++++++++++++++----- .../main/res/layout/dialog_poll_create.xml | 1 - 5 files changed, 82 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index f0b81e973..e8ca339ef 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -31,11 +31,14 @@ class PollCreateOptionViewHolder( itemsListener.onRemoveOptionsItemClick(pollCreateOptionItem, position) } - textListener = getTextWatcher(pollCreateOptionItem) + textListener = getTextWatcher(pollCreateOptionItem, itemsListener) binding.pollOptionText.addTextChangedListener(textListener) } - private fun getTextWatcher(pollCreateOptionItem: PollCreateOptionItem) = + private fun getTextWatcher( + pollCreateOptionItem: PollCreateOptionItem, + itemsListener: PollCreateOptionsItemListener + ) = object : TextWatcher { override fun afterTextChanged(s: Editable) { // unused atm @@ -47,6 +50,8 @@ class PollCreateOptionViewHolder( override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { pollCreateOptionItem.pollOption = option.toString() + + itemsListener.onOptionsItemTextChanged(pollCreateOptionItem) } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt index 751c95d4b..5cb0c53b4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt @@ -3,4 +3,6 @@ package com.nextcloud.talk.polls.adapters interface PollCreateOptionsItemListener { fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) + + fun onOptionsItemTextChanged(pollCreateOptionItem: PollCreateOptionItem) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 365506133..35bd6648f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -5,14 +5,17 @@ import android.app.Dialog import android.os.Bundle import android.text.Editable import android.text.TextWatcher +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector +import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollCreateBinding import com.nextcloud.talk.polls.adapters.PollCreateOptionItem @@ -78,6 +81,7 @@ class PollCreateDialogFragment( private fun setupListeners() { binding.pollAddOptionsItem.setOnClickListener { viewModel.addOption() + adapter?.itemCount?.minus(1)?.let { it -> binding.pollCreateOptionsList.scrollToPosition(it) } } binding.pollDismiss.setOnClickListener { @@ -117,41 +121,32 @@ class PollCreateDialogFragment( private fun setupStateObserver() { viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { - // PollCreateViewModel.InitialState -> showInitial() is PollCreateViewModel.PollCreatedState -> dismiss() - is PollCreateViewModel.PollCreationFailedState -> dismiss() - is PollCreateViewModel.PollCreatingState -> updateDialog(state) + is PollCreateViewModel.PollCreationFailedState -> showError() + is PollCreateViewModel.PollCreationState -> updateButtons(state) } } - // viewModel.state.observe(this) { state -> - // when (state) { - // MessageSearchViewModel.InitialState -> showInitial() - // MessageSearchViewModel.EmptyState -> showEmpty() - // is MessageSearchViewModel.LoadedState -> showLoaded(state) - // MessageSearchViewModel.LoadingState -> showLoading() - // MessageSearchViewModel.ErrorState -> showError() - // is MessageSearchViewModel.FinishedState -> onFinish() - // } - // } } - private fun updateDialog(state: PollCreateViewModel.PollCreatingState) { - // binding.pollCreateQuestion.setText(state.question) - // - // adapter!!.updateOptionsList(state.options) - // - // binding.pollPrivatePollCheckbox.isChecked = state.privatePoll - // binding.pollMultipleAnswersCheckbox.isChecked = state.multipleAnswer + private fun updateButtons(state: PollCreateViewModel.PollCreationState) { + binding.pollAddOptionsItem.isEnabled = state.enableAddOptionButton + binding.pollCreateButton.isEnabled = state.enableCreatePollButton } - private fun showInitial() { - binding.pollCreateButton.isEnabled = false + private fun showError() { + dismiss() + Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show() + Log.e(TAG, "Failed to create poll") } override fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) { viewModel.removeOption(pollCreateOptionItem) } + override fun onOptionsItemTextChanged(pollCreateOptionItem: PollCreateOptionItem) { + viewModel.optionsItemTextChanged() + } + /** * Fragment creator */ diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index 05f3fabe4..341618d93 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -16,10 +16,14 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi private lateinit var roomToken: String - // private var _options: MutableLiveData> = - // MutableLiveData>() - // val options: LiveData> - // get() = _options + sealed interface ViewState + open class PollCreationState(val enableAddOptionButton: Boolean, val enableCreatePollButton: Boolean) : ViewState + object PollCreatedState : ViewState + object PollCreationFailedState : ViewState + + private val _viewState: MutableLiveData = MutableLiveData(PollCreationState(true, false)) + val viewState: LiveData + get() = _viewState private var _options: MutableLiveData> = MutableLiveData>() @@ -31,27 +35,18 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi get() = _question private var _privatePoll: MutableLiveData = MutableLiveData() - var privatePoll: LiveData = _privatePoll + val privatePoll: LiveData get() = _privatePoll private var _multipleAnswer: MutableLiveData = MutableLiveData() - var multipleAnswer: LiveData = _multipleAnswer + val multipleAnswer: LiveData get() = _multipleAnswer - sealed interface ViewState - object InitialState : ViewState - open class PollCreatingState() : ViewState - open class PollCreatedState() : ViewState - open class PollCreationFailedState() : ViewState - - private val _viewState: MutableLiveData = MutableLiveData(InitialState) - val viewState: LiveData - get() = _viewState - private var disposable: Disposable? = null fun initialize(roomToken: String) { this.roomToken = roomToken + updateCreationState() } override fun onCleared() { @@ -64,12 +59,14 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi val currentOptions: ArrayList = _options.value ?: ArrayList() currentOptions.add(item) _options.value = currentOptions + updateCreationState() } fun removeOption(item: PollCreateOptionItem) { val currentOptions: ArrayList = _options.value ?: ArrayList() currentOptions.remove(item) _options.value = currentOptions + updateCreationState() } fun createPoll() { @@ -83,9 +80,9 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi resultMode = 1 } - if (question.value?.isNotEmpty() == true && _options.value?.isNotEmpty() == true) { + if (_question.value?.isNotEmpty() == true && _options.value?.isNotEmpty() == true) { repository.createPoll( - roomToken, question.value!!, _options.value!!.map { it.pollOption }, resultMode, + roomToken, _question.value!!, _options.value!!.map { it.pollOption }, resultMode, maxVotes ) ?.doOnSubscribe { disposable = it } @@ -97,6 +94,7 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi fun setQuestion(question: String) { _question.value = question + updateCreationState() } fun setPrivatePoll(checked: Boolean) { @@ -107,6 +105,44 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi _multipleAnswer.value = checked } + fun optionsItemTextChanged() { + updateCreationState() + } + + private fun updateCreationState() { + _viewState.value = PollCreationState(enableAddOptionButton(), enableCreatePollButton()) + } + + private fun enableCreatePollButton(): Boolean { + return _question.value?.isNotEmpty() == true && atLeastTwoOptionsAreFilled() + } + + private fun atLeastTwoOptionsAreFilled(): Boolean { + if (_options.value != null) { + var filledOptions = 0 + _options.value?.forEach { + if (it.pollOption.isNotEmpty()) { + filledOptions++ + } + if (filledOptions >= 2) { + return true + } + } + } + return false + } + + private fun enableAddOptionButton(): Boolean { + if (_options.value != null && _options.value?.size != 0) { + _options.value?.forEach { + if (it.pollOption.isBlank()) { + return false + } + } + } + return true + } + inner class PollObserver : Observer { lateinit var poll: Poll @@ -118,11 +154,11 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi } override fun onError(e: Throwable) { - _viewState.value = PollCreationFailedState() + _viewState.value = PollCreationFailedState } override fun onComplete() { - _viewState.value = PollCreatedState() + _viewState.value = PollCreatedState } } diff --git a/app/src/main/res/layout/dialog_poll_create.xml b/app/src/main/res/layout/dialog_poll_create.xml index 50b3e77a0..23989ba33 100644 --- a/app/src/main/res/layout/dialog_poll_create.xml +++ b/app/src/main/res/layout/dialog_poll_create.xml @@ -31,7 +31,6 @@ - Date: Wed, 22 Jun 2022 13:33:14 +0200 Subject: [PATCH 18/92] add UI logic + close poll system message add close poll button (wip/temporarily?) add "close poll" system message add UI related logic to PollMainViewModel add placeholder for pollDetails in UI Signed-off-by: Marcel Hibbe --- .../talk/models/json/chat/ChatMessage.kt | 3 +- .../EnumSystemMessageTypeConverter.kt | 3 ++ .../polls/adapters/PollResultViewHolder.kt | 10 +++- .../talk/polls/adapters/PollResultsAdapter.kt | 5 +- .../talk/polls/repositories/PollRepository.kt | 10 ++-- .../polls/repositories/PollRepositoryImpl.kt | 12 +++++ .../talk/polls/ui/PollMainDialogFragment.kt | 4 +- .../talk/polls/ui/PollResultsFragment.kt | 51 +++++++++++++------ .../talk/polls/ui/PollVoteFragment.kt | 4 +- .../polls/viewmodels/PollMainViewModel.kt | 35 +++++++++++-- .../main/res/layout/dialog_poll_results.xml | 12 +++++ app/src/main/res/layout/poll_result_item.xml | 15 ++++++ 12 files changed, 131 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index 312ad14a5..c47293fd6 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt @@ -491,7 +491,8 @@ data class ChatMessage( REACTION, REACTION_DELETED, REACTION_REVOKED, - POLL_VOTED + POLL_VOTED, + POLL_CLOSED } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt index 3a8049a78..20e525110 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt @@ -65,6 +65,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERAT import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.OBJECT_SHARED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_REMOVED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_SET +import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.POLL_CLOSED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.POLL_VOTED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.REACTION_DELETED @@ -169,6 +170,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter REACTION_DELETED "reaction_revoked" -> REACTION_REVOKED "poll_voted" -> POLL_VOTED + "poll_closed" -> POLL_CLOSED else -> DUMMY } } @@ -227,6 +229,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter return "reaction_deleted" REACTION_REVOKED -> return "reaction_revoked" POLL_VOTED -> return "poll_voted" + POLL_CLOSED -> return "poll_closed" else -> return "" } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index ce928340d..cee0d95ad 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -1,11 +1,13 @@ package com.nextcloud.talk.polls.adapters import android.annotation.SuppressLint +import android.view.View import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.databinding.PollResultItemBinding class PollResultViewHolder( - private val binding: PollResultItemBinding + private val binding: PollResultItemBinding, + private val showDetails: Boolean ) : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") @@ -14,5 +16,11 @@ class PollResultViewHolder( binding.pollOptionText.text = pollResultItem.pollOption binding.pollOptionPercentText.text = pollResultItem.pollPercent.toString() + "%" binding.pollOptionBar.progress = pollResultItem.pollPercent + + if (showDetails) { + binding.pollOptionDetail.visibility = View.VISIBLE + } else { + binding.pollOptionDetail.visibility = View.GONE + } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index 8c2471f94..039ec4900 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -6,13 +6,14 @@ import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.databinding.PollResultItemBinding class PollResultsAdapter( - private val clickListener: PollResultItemClickListener + private val clickListener: PollResultItemClickListener, + private val showDetails: Boolean ) : RecyclerView.Adapter() { internal var list: MutableList = ArrayList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollResultViewHolder { val itemBinding = PollResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return PollResultViewHolder(itemBinding) + return PollResultViewHolder(itemBinding, showDetails) } override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt index 4654e170f..b2f9c0cf8 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -5,10 +5,6 @@ import io.reactivex.Observable interface PollRepository { - fun getPoll(roomToken: String, pollId: String): Observable? - - fun vote(roomToken: String, pollId: String, option: Int): Observable? - fun createPoll( roomToken: String, question: String, @@ -16,4 +12,10 @@ interface PollRepository { resultMode: Int, maxVotes: Int ): Observable? + + fun getPoll(roomToken: String, pollId: String): Observable? + + fun vote(roomToken: String, pollId: String, option: Int): Observable? + + fun closePoll(roomToken: String, pollId: String): Observable? } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index e95843190..3c5d73bb4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -102,6 +102,18 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid ).map { mapToPoll(it.ocs?.data!!) } } + override fun closePoll(roomToken: String, pollId: String): Observable { + + return ncApi.closePoll( + credentials, + ApiUtils.getUrlForPoll( + currentUserProvider.currentUser?.baseUrl, + roomToken, + pollId + ), + ).map { mapToPoll(it.ocs?.data!!) } + } + companion object { private fun mapToPoll(pollResponse: PollResponse): Poll { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index f7bb368c5..3be8fa879 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -60,7 +60,7 @@ class PollMainDialogFragment( when (state) { PollMainViewModel.InitialState -> {} - is PollMainViewModel.PollVotedState -> { + is PollMainViewModel.PollResultState -> { if (state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { showVoteFragment() } else { @@ -68,7 +68,7 @@ class PollMainDialogFragment( } } - is PollMainViewModel.PollUnvotedState -> { + is PollMainViewModel.PollVoteState -> { if (state.poll.status == Poll.STATUS_CLOSED) { showResultsFragment() } else { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 4ed2c9404..1aa27c153 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -77,26 +77,23 @@ class PollResultsFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - adapter = PollResultsAdapter(this) - _binding?.pollResultsList?.adapter = adapter - _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) - parentViewModel.viewState.observe(viewLifecycleOwner) { state -> - if (state is PollMainViewModel.PollVotedState && - state.poll.resultMode == Poll.RESULT_MODE_PUBLIC - ) { - + if (state is PollMainViewModel.PollResultState) { + initAdapter(state.showDetails) initPollResults(state.poll) initAmountVotersInfo(state) - initEditButton(state) - } else if (state is PollMainViewModel.PollUnvotedState && - state.poll.status == Poll.STATUS_CLOSED - ) { - Log.d(TAG, "show results also if self never voted") + initEditButton(state.showEditButton) + initCloseButton(state.showCloseButton) } } } + private fun initAdapter(showDetails: Boolean) { + adapter = PollResultsAdapter(this, showDetails) + _binding?.pollResultsList?.adapter = adapter + _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) + } + private fun initPollResults(poll: Poll) { if (poll.details != null) { val votersAmount = poll.details.size @@ -128,15 +125,26 @@ class PollResultsFragment( } } - private fun initAmountVotersInfo(state: PollMainViewModel.PollVotedState) { + private fun initAmountVotersInfo(state: PollMainViewModel.PollResultState) { _binding?.pollAmountVoters?.text = String.format( resources.getString(R.string.polls_amount_voters), state.poll.numVoters ) } - private fun initEditButton(state: PollMainViewModel.PollVotedState) { - if (state.poll.status == Poll.STATUS_OPEN && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC) { + // private fun initEditButton(state: PollMainViewModel.PollResultState) { + // if (state.poll.status == Poll.STATUS_OPEN && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC) { + // _binding?.editVoteButton?.visibility = View.VISIBLE + // _binding?.editVoteButton?.setOnClickListener { + // parentViewModel.edit() + // } + // } else { + // _binding?.editVoteButton?.visibility = View.GONE + // } + // } + + private fun initEditButton(showEditButton: Boolean) { + if (showEditButton) { _binding?.editVoteButton?.visibility = View.VISIBLE _binding?.editVoteButton?.setOnClickListener { parentViewModel.edit() @@ -146,6 +154,17 @@ class PollResultsFragment( } } + private fun initCloseButton(showCloseButton: Boolean) { + if (showCloseButton) { + _binding?.closeVoteButton?.visibility = View.VISIBLE + _binding?.closeVoteButton?.setOnClickListener { + parentViewModel.closePoll() + } + } else { + _binding?.closeVoteButton?.visibility = View.GONE + } + } + override fun onClick(pollResultItem: PollResultItem) { Log.d(TAG, "click..") } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 0f74d7af6..668650b05 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -71,7 +71,7 @@ class PollVoteFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) parentViewModel.viewState.observe(viewLifecycleOwner) { state -> - if (state is PollMainViewModel.PollUnvotedState) { + if (state is PollMainViewModel.PollVoteState) { val poll = state.poll binding.radioGroup.removeAllViews() poll.options?.map { option -> @@ -80,7 +80,7 @@ class PollVoteFragment( radioButton.id = index binding.radioGroup.addView(radioButton) } - } else if (state is PollMainViewModel.PollVotedState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { + } else if (state is PollMainViewModel.PollResultState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { Log.d(TAG, "show vote screen also for resultMode hidden poll when already voted") // TODO: other text for submit button } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 28594beab..8d15d3e4b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.repositories.PollRepository +import com.nextcloud.talk.utils.database.user.UserUtils import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable @@ -25,6 +26,9 @@ import javax.inject.Inject */ class PollMainViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { + @Inject + lateinit var userUtils: UserUtils + private lateinit var roomToken: String private lateinit var pollId: String @@ -32,8 +36,13 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito sealed interface ViewState object InitialState : ViewState - open class PollUnvotedState(val poll: Poll) : ViewState - open class PollVotedState(val poll: Poll) : ViewState + open class PollVoteState(val poll: Poll) : ViewState + open class PollResultState( + val poll: Poll, + val showDetails: Boolean, + val showEditButton: Boolean, + val showCloseButton: Boolean + ) : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) val viewState: LiveData @@ -65,6 +74,14 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito ?.subscribe(PollObserver()) } + fun closePoll() { + repository.closePoll(roomToken, pollId) + ?.doOnSubscribe { disposable = it } + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(PollObserver()) + } + override fun onCleared() { super.onCleared() disposable?.dispose() @@ -86,16 +103,24 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito override fun onComplete() { if (editPoll) { - _viewState.value = PollUnvotedState(poll) + _viewState.value = PollVoteState(poll) editPoll = false } else if (poll.votedSelf.isNullOrEmpty()) { - _viewState.value = PollUnvotedState(poll) + _viewState.value = PollVoteState(poll) } else { - _viewState.value = PollVotedState(poll) + val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC + val showDetails = poll.status == Poll.STATUS_CLOSED && poll.resultMode == Poll.RESULT_MODE_PUBLIC + val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) + + _viewState.value = PollResultState(poll, showDetails, showEditButton, showCloseButton) } } } + fun isPollCreatedByCurrentUser(poll: Poll): Boolean { + return userUtils.currentUser?.userId == poll.actorId + } + companion object { private val TAG = PollMainViewModel::class.java.simpleName } diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 77a5d7430..5b8008e31 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -48,6 +48,18 @@ app:layout_constraintTop_toBottomOf="@+id/poll_results_list_wrapper" tools:text="Poll results - 93 votes" /> + + + + + + + + + From fbd5e5f5ede71fe4182fd78c6fe61389b7182aa0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 22 Jun 2022 16:37:58 +0200 Subject: [PATCH 19/92] show voting screen for open private polls Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollMainDialogFragment.kt | 20 +++----------- .../talk/polls/ui/PollResultsFragment.kt | 6 ++--- .../talk/polls/ui/PollVoteFragment.kt | 26 ++++++++++-------- .../polls/viewmodels/PollMainViewModel.kt | 27 ++++++++++++++----- .../main/res/layout/dialog_poll_results.xml | 2 +- app/src/main/res/layout/dialog_poll_vote.xml | 20 +++++++++++--- 6 files changed, 59 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 3be8fa879..1ee30ee68 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -12,7 +12,6 @@ import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollMainBinding -import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import javax.inject.Inject @@ -59,22 +58,9 @@ class PollMainDialogFragment( viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { PollMainViewModel.InitialState -> {} - - is PollMainViewModel.PollResultState -> { - if (state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { - showVoteFragment() - } else { - showResultsFragment() - } - } - - is PollMainViewModel.PollVoteState -> { - if (state.poll.status == Poll.STATUS_CLOSED) { - showResultsFragment() - } else { - showVoteFragment() - } - } + is PollMainViewModel.PollVoteHiddenState -> showVoteFragment() + is PollMainViewModel.PollVoteState -> showVoteFragment() + is PollMainViewModel.PollResultState -> showResultsFragment() } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 1aa27c153..b4262060c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -156,12 +156,12 @@ class PollResultsFragment( private fun initCloseButton(showCloseButton: Boolean) { if (showCloseButton) { - _binding?.closeVoteButton?.visibility = View.VISIBLE - _binding?.closeVoteButton?.setOnClickListener { + _binding?.pollResultsClosePollButton?.visibility = View.VISIBLE + _binding?.pollResultsClosePollButton?.setOnClickListener { parentViewModel.closePoll() } } else { - _binding?.closeVoteButton?.visibility = View.GONE + _binding?.pollResultsClosePollButton?.visibility = View.GONE } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 668650b05..d3b700811 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -72,17 +72,11 @@ class PollVoteFragment( super.onViewCreated(view, savedInstanceState) parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollVoteState) { - val poll = state.poll - binding.radioGroup.removeAllViews() - poll.options?.map { option -> - RadioButton(context).apply { text = option } - }?.forEachIndexed { index, radioButton -> - radioButton.id = index - binding.radioGroup.addView(radioButton) - } - } else if (state is PollMainViewModel.PollResultState && state.poll.resultMode == Poll.RESULT_MODE_HIDDEN) { - Log.d(TAG, "show vote screen also for resultMode hidden poll when already voted") - // TODO: other text for submit button + initPollOptions(state.poll) + binding.pollVoteHiddenHint.visibility = View.GONE + } else if (state is PollMainViewModel.PollVoteHiddenState) { + initPollOptions(state.poll) + binding.pollVoteHiddenHint.visibility = View.VISIBLE } } @@ -109,6 +103,16 @@ class PollVoteFragment( } } + private fun initPollOptions(poll: Poll) { + binding.radioGroup.removeAllViews() + poll.options?.map { option -> + RadioButton(context).apply { text = option } + }?.forEachIndexed { index, radioButton -> + radioButton.id = index + binding.radioGroup.addView(radioButton) + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 8d15d3e4b..3e7955d23 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -37,6 +37,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito sealed interface ViewState object InitialState : ViewState open class PollVoteState(val poll: Poll) : ViewState + open class PollVoteHiddenState(val poll: Poll) : ViewState open class PollResultState( val poll: Poll, val showDetails: Boolean, @@ -102,21 +103,35 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito } override fun onComplete() { - if (editPoll) { + if (votedForOpenHiddenPoll(poll)) { + _viewState.value = PollVoteHiddenState(poll) + } else if (editPoll && poll.status == Poll.STATUS_OPEN) { _viewState.value = PollVoteState(poll) editPoll = false + } else if (poll.status == Poll.STATUS_CLOSED || poll.votedSelf?.isNotEmpty() == true) { + setPollResultState(poll) } else if (poll.votedSelf.isNullOrEmpty()) { _viewState.value = PollVoteState(poll) } else { - val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC - val showDetails = poll.status == Poll.STATUS_CLOSED && poll.resultMode == Poll.RESULT_MODE_PUBLIC - val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) - - _viewState.value = PollResultState(poll, showDetails, showEditButton, showCloseButton) + Log.w(TAG, "unknown poll state") } } } + fun setPollResultState(poll: Poll) { + val showDetails = poll.status == Poll.STATUS_CLOSED && poll.resultMode == Poll.RESULT_MODE_PUBLIC + val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC + val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) + + _viewState.value = PollResultState(poll, showDetails, showEditButton, showCloseButton) + } + + fun votedForOpenHiddenPoll(poll: Poll): Boolean { + return poll.status == Poll.STATUS_OPEN && + poll.resultMode == Poll.RESULT_MODE_HIDDEN && + poll.votedSelf?.isNotEmpty() == true + } + fun isPollCreatedByCurrentUser(poll: Poll): Boolean { return userUtils.currentUser?.userId == poll.actorId } diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 5b8008e31..49308a76c 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -49,7 +49,7 @@ tools:text="Poll results - 93 votes" /> + tools:layout_width="match_parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + From 8c898404d1413755b0e77ef7ee496b52acb0b738 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 22 Jun 2022 19:55:41 +0200 Subject: [PATCH 20/92] move details text to PollMainViewModel Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollMainDialogFragment.kt | 29 +++++++++++++++---- .../talk/polls/ui/PollResultsFragment.kt | 22 +------------- .../talk/polls/ui/PollVoteFragment.kt | 4 +-- .../polls/viewmodels/PollMainViewModel.kt | 10 +++---- app/src/main/res/layout/dialog_poll_main.xml | 18 +++++++++--- .../main/res/layout/dialog_poll_results.xml | 11 ------- app/src/main/res/layout/dialog_poll_vote.xml | 11 ------- 7 files changed, 46 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 1ee30ee68..ba7f0d57d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -10,8 +10,10 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector +import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollMainBinding +import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import javax.inject.Inject @@ -58,16 +60,23 @@ class PollMainDialogFragment( viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { PollMainViewModel.InitialState -> {} - is PollMainViewModel.PollVoteHiddenState -> showVoteFragment() - is PollMainViewModel.PollVoteState -> showVoteFragment() - is PollMainViewModel.PollResultState -> showResultsFragment() + is PollMainViewModel.PollVoteHiddenState -> { + binding.pollDetailsText.visibility = View.VISIBLE + binding.pollDetailsText.text = "You already voted for this private poll" + showVoteScreen() + } + is PollMainViewModel.PollVoteState -> { + binding.pollDetailsText.visibility = View.GONE + showVoteScreen() + } + is PollMainViewModel.PollResultState -> showResultsScreen(state.poll) } } viewModel.initialize(roomToken, pollId) } - private fun showVoteFragment() { + private fun showVoteScreen() { val contentFragment = PollVoteFragment( viewModel, roomToken, @@ -78,7 +87,9 @@ class PollMainDialogFragment( transaction.commit() } - private fun showResultsFragment() { + private fun showResultsScreen(poll: Poll) { + initVotersAmount(poll.numVoters) + val contentFragment = PollResultsFragment( viewModel, roomToken, @@ -89,6 +100,14 @@ class PollMainDialogFragment( transaction.commit() } + private fun initVotersAmount(numVoters: Int) { + binding.pollDetailsText.visibility = View.VISIBLE + binding.pollDetailsText.text = String.format( + resources.getString(R.string.polls_amount_voters), + numVoters + ) + } + /** * Fragment creator */ diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index b4262060c..a40cd0156 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -30,7 +30,6 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector -import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollResultsBinding import com.nextcloud.talk.polls.adapters.PollResultItem @@ -79,9 +78,8 @@ class PollResultsFragment( parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollResultState) { - initAdapter(state.showDetails) + initAdapter(state.showParticipants) initPollResults(state.poll) - initAmountVotersInfo(state) initEditButton(state.showEditButton) initCloseButton(state.showCloseButton) } @@ -125,24 +123,6 @@ class PollResultsFragment( } } - private fun initAmountVotersInfo(state: PollMainViewModel.PollResultState) { - _binding?.pollAmountVoters?.text = String.format( - resources.getString(R.string.polls_amount_voters), - state.poll.numVoters - ) - } - - // private fun initEditButton(state: PollMainViewModel.PollResultState) { - // if (state.poll.status == Poll.STATUS_OPEN && state.poll.resultMode == Poll.RESULT_MODE_PUBLIC) { - // _binding?.editVoteButton?.visibility = View.VISIBLE - // _binding?.editVoteButton?.setOnClickListener { - // parentViewModel.edit() - // } - // } else { - // _binding?.editVoteButton?.visibility = View.GONE - // } - // } - private fun initEditButton(showEditButton: Boolean) { if (showEditButton) { _binding?.editVoteButton?.visibility = View.VISIBLE diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index d3b700811..f6fbd2541 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -73,10 +73,10 @@ class PollVoteFragment( parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollVoteState) { initPollOptions(state.poll) - binding.pollVoteHiddenHint.visibility = View.GONE + // binding.pollVoteHiddenHint.visibility = View.GONE } else if (state is PollMainViewModel.PollVoteHiddenState) { initPollOptions(state.poll) - binding.pollVoteHiddenHint.visibility = View.VISIBLE + // binding.pollVoteHiddenHint.visibility = View.VISIBLE } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 3e7955d23..35f166344 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -40,7 +40,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito open class PollVoteHiddenState(val poll: Poll) : ViewState open class PollResultState( val poll: Poll, - val showDetails: Boolean, + val showParticipants: Boolean, val showEditButton: Boolean, val showCloseButton: Boolean ) : ViewState @@ -59,7 +59,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito } fun voted() { - loadPoll() // TODO load other view + loadPoll() } fun edit() { @@ -118,7 +118,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito } } - fun setPollResultState(poll: Poll) { + private fun setPollResultState(poll: Poll) { val showDetails = poll.status == Poll.STATUS_CLOSED && poll.resultMode == Poll.RESULT_MODE_PUBLIC val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) @@ -126,13 +126,13 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito _viewState.value = PollResultState(poll, showDetails, showEditButton, showCloseButton) } - fun votedForOpenHiddenPoll(poll: Poll): Boolean { + private fun votedForOpenHiddenPoll(poll: Poll): Boolean { return poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_HIDDEN && poll.votedSelf?.isNotEmpty() == true } - fun isPollCreatedByCurrentUser(poll: Poll): Boolean { + private fun isPollCreatedByCurrentUser(poll: Poll): Boolean { return userUtils.currentUser?.userId == poll.actorId } diff --git a/app/src/main/res/layout/dialog_poll_main.xml b/app/src/main/res/layout/dialog_poll_main.xml index c03afd25d..59e5e8a84 100644 --- a/app/src/main/res/layout/dialog_poll_main.xml +++ b/app/src/main/res/layout/dialog_poll_main.xml @@ -21,14 +21,14 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:padding="@dimen/standard_padding" tools:background="@color/white"> + + diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 49308a76c..211dfb61a 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -21,7 +21,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:tools="http://schemas.android.com/tools" - android:padding="@dimen/standard_padding" tools:background="@color/white"> - - - - From 8541ebbfc0a21f1c27e67e88f05d8bbe0c449765 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 22 Jun 2022 20:34:50 +0200 Subject: [PATCH 21/92] add ability to close poll in PollVoteFragment this adds duplicate button logic atm. might need to be improved.. Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollVoteFragment.kt | 17 +++++++++++++--- .../polls/viewmodels/PollMainViewModel.kt | 20 ++++++++++++++----- .../main/res/layout/dialog_poll_results.xml | 8 +++----- app/src/main/res/layout/dialog_poll_vote.xml | 15 ++++++++++++-- app/src/main/res/values/strings.xml | 1 + 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index f6fbd2541..44c2a4098 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -73,10 +73,10 @@ class PollVoteFragment( parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollVoteState) { initPollOptions(state.poll) - // binding.pollVoteHiddenHint.visibility = View.GONE + initCloseButton(state.showCloseButton) } else if (state is PollMainViewModel.PollVoteHiddenState) { initPollOptions(state.poll) - // binding.pollVoteHiddenHint.visibility = View.VISIBLE + initCloseButton(state.showCloseButton) } } @@ -98,7 +98,7 @@ class PollVoteFragment( } // todo observe viewmodel checked, set view checked with it - binding.submitVote.setOnClickListener { + binding.pollVoteSubmitButton.setOnClickListener { viewModel.vote(roomToken, pollId, binding.radioGroup.checkedRadioButtonId) } } @@ -113,6 +113,17 @@ class PollVoteFragment( } } + private fun initCloseButton(showCloseButton: Boolean) { + if (showCloseButton) { + _binding?.pollVoteClosePollButton?.visibility = View.VISIBLE + _binding?.pollVoteClosePollButton?.setOnClickListener { + parentViewModel.closePoll() + } + } else { + _binding?.pollVoteClosePollButton?.visibility = View.GONE + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 35f166344..909f108a6 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -36,8 +36,16 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito sealed interface ViewState object InitialState : ViewState - open class PollVoteState(val poll: Poll) : ViewState - open class PollVoteHiddenState(val poll: Poll) : ViewState + open class PollVoteState( + val poll: Poll, + val showCloseButton: Boolean + ) : ViewState + + open class PollVoteHiddenState( + val poll: Poll, + val showCloseButton: Boolean + ) : ViewState + open class PollResultState( val poll: Poll, val showParticipants: Boolean, @@ -103,15 +111,17 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito } override fun onComplete() { + val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) + if (votedForOpenHiddenPoll(poll)) { - _viewState.value = PollVoteHiddenState(poll) + _viewState.value = PollVoteHiddenState(poll, showCloseButton) } else if (editPoll && poll.status == Poll.STATUS_OPEN) { - _viewState.value = PollVoteState(poll) + _viewState.value = PollVoteState(poll, showCloseButton) editPoll = false } else if (poll.status == Poll.STATUS_CLOSED || poll.votedSelf?.isNotEmpty() == true) { setPollResultState(poll) } else if (poll.votedSelf.isNullOrEmpty()) { - _viewState.value = PollVoteState(poll) + _viewState.value = PollVoteState(poll, showCloseButton) } else { Log.w(TAG, "unknown poll state") } diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 211dfb61a..06443366b 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -41,19 +41,17 @@ android:id="@+id/poll_results_close_poll_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:text="Close" - android:theme="@style/Button.Primary" + android:text="@string/polls_close_poll" + style="@style/OutlinedButton" + android:layout_marginEnd="@dimen/standard_margin" app:cornerRadius="@dimen/button_corner_radius" app:layout_constraintEnd_toStartOf="@+id/edit_vote_button" app:layout_constraintTop_toBottomOf="@+id/poll_results_list_wrapper" /> - + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cab13f522..644436c16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -539,6 +539,7 @@ Poll results - %1$s votes Add Option + Close Poll Attachments From acda3d283b3b0076883932f7ea686b128832af21 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Jun 2022 13:42:57 +0200 Subject: [PATCH 22/92] add multiselect for poll options Signed-off-by: Marcel Hibbe --- .../talk/polls/repositories/PollRepository.kt | 2 +- .../polls/repositories/PollRepositoryImpl.kt | 4 +- .../talk/polls/ui/PollVoteFragment.kt | 40 +++++++++++++++---- .../polls/viewmodels/PollVoteViewModel.kt | 30 +++++++++----- app/src/main/res/layout/dialog_poll_vote.xml | 34 ++++++++++++---- 5 files changed, 81 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt index b2f9c0cf8..003651af0 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -15,7 +15,7 @@ interface PollRepository { fun getPoll(roomToken: String, pollId: String): Observable? - fun vote(roomToken: String, pollId: String, option: Int): Observable? + fun vote(roomToken: String, pollId: String, options: List): Observable? fun closePoll(roomToken: String, pollId: String): Observable? } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 3c5d73bb4..d5329869a 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -89,7 +89,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid // ) } - override fun vote(roomToken: String, pollId: String, option: Int): Observable? { + override fun vote(roomToken: String, pollId: String, options: List): Observable? { return ncApi.votePoll( credentials, @@ -98,7 +98,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid roomToken, pollId ), - arrayOf(option).asList() + options ).map { mapToPoll(it.ocs?.data!!) } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 44c2a4098..fef5a0c99 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -26,6 +26,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.CheckBox import android.widget.RadioButton import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider @@ -92,24 +93,47 @@ class PollVoteFragment( } } - binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> + binding.pollVoteRadioGroup.setOnCheckedChangeListener { group, checkedId -> // todo set selected in viewmodel. Log.d("bb", "click") } // todo observe viewmodel checked, set view checked with it binding.pollVoteSubmitButton.setOnClickListener { - viewModel.vote(roomToken, pollId, binding.radioGroup.checkedRadioButtonId) + // viewModel.vote(roomToken, pollId, binding.pollVoteRadioGroup.checkedRadioButtonId) + viewModel.vote(roomToken, pollId) } } private fun initPollOptions(poll: Poll) { - binding.radioGroup.removeAllViews() - poll.options?.map { option -> - RadioButton(context).apply { text = option } - }?.forEachIndexed { index, radioButton -> - radioButton.id = index - binding.radioGroup.addView(radioButton) + poll.votedSelf?.let { viewModel.initSelectedOptions(it as ArrayList) } + + + if (poll.maxVotes == 1) { + binding.pollVoteRadioGroup.removeAllViews() + poll.options?.map { option -> + RadioButton(context).apply { text = option } + }?.forEachIndexed { index, radioButton -> + radioButton.id = index + binding.pollVoteRadioGroup.addView(radioButton) + } + } else { + binding.voteOptionsCheckboxesWrapper.removeAllViews() + poll.options?.map { option -> + CheckBox(context).apply { text = option } + }?.forEachIndexed { index, checkBox -> + checkBox.id = index + binding.voteOptionsCheckboxesWrapper.addView(checkBox) + + checkBox.isChecked = viewModel.selectedOptions.value?.contains(index) == true + checkBox.setOnCheckedChangeListener { buttonView, isChecked -> + if (isChecked) { + viewModel.selectOption(index) + } else { + viewModel.deSelectOption(index) + } + } + } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 7de9f7243..41ffe54df 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -46,20 +46,30 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito private var disposable: Disposable? = null - private val _selectedOptions: MutableLiveData> = MutableLiveData(emptyList()) - val selectedOptions: LiveData> + private val _selectedOptions: MutableLiveData> = MutableLiveData(emptyList()) + val selectedOptions: LiveData> get() = _selectedOptions - fun selectOption(option: String) { - _selectedOptions.value = listOf(option) + fun initSelectedOptions(selectedOptions: List) { + _selectedOptions.value = selectedOptions } - fun vote(roomToken: String, pollId: String, option: Int) { - repository.vote(roomToken, pollId, option) - ?.doOnSubscribe { disposable = it } - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(PollObserver()) + fun selectOption(option: Int) { + _selectedOptions.value = _selectedOptions.value?.plus(option) + } + + fun deSelectOption(option: Int) { + _selectedOptions.value = _selectedOptions.value?.minus(option) + } + + fun vote(roomToken: String, pollId: String) { + if (!_selectedOptions.value.isNullOrEmpty()) { + repository.vote(roomToken, pollId, _selectedOptions.value!!) + ?.doOnSubscribe { disposable = it } + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(PollObserver()) + } } override fun onCleared() { diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 004b8db4e..407d9a43f 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -23,14 +23,32 @@ xmlns:tools="http://schemas.android.com/tools" tools:background="@color/white"> - + app:layout_constraintTop_toTopOf="parent" + android:orientation="vertical"> + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/vote_options_wrapper" /> + app:layout_constraintTop_toBottomOf="@+id/vote_options_wrapper" /> From 2da4c37a5282eb2ac26897bdbeb597af69d452ee Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Jun 2022 14:28:22 +0200 Subject: [PATCH 23/92] remove liveData for selectedOptions Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/ui/PollVoteFragment.kt | 2 +- .../talk/polls/viewmodels/PollVoteViewModel.kt | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index fef5a0c99..7fd31739a 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -125,7 +125,7 @@ class PollVoteFragment( checkBox.id = index binding.voteOptionsCheckboxesWrapper.addView(checkBox) - checkBox.isChecked = viewModel.selectedOptions.value?.contains(index) == true + checkBox.isChecked = viewModel.selectedOptions.contains(index) == true checkBox.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) { viewModel.selectOption(index) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 41ffe54df..67ffb5fbb 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -46,25 +46,25 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito private var disposable: Disposable? = null - private val _selectedOptions: MutableLiveData> = MutableLiveData(emptyList()) - val selectedOptions: LiveData> + private var _selectedOptions: List = emptyList() + val selectedOptions: List get() = _selectedOptions fun initSelectedOptions(selectedOptions: List) { - _selectedOptions.value = selectedOptions + _selectedOptions = selectedOptions } fun selectOption(option: Int) { - _selectedOptions.value = _selectedOptions.value?.plus(option) + _selectedOptions = _selectedOptions.plus(option) } fun deSelectOption(option: Int) { - _selectedOptions.value = _selectedOptions.value?.minus(option) + _selectedOptions = _selectedOptions.minus(option) } fun vote(roomToken: String, pollId: String) { - if (!_selectedOptions.value.isNullOrEmpty()) { - repository.vote(roomToken, pollId, _selectedOptions.value!!) + if (_selectedOptions.isNotEmpty()) { + repository.vote(roomToken, pollId, _selectedOptions) ?.doOnSubscribe { disposable = it } ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) From a4c48b11d65d41311f725093c4da55689a80a8e8 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Jun 2022 15:11:48 +0200 Subject: [PATCH 24/92] fix radiobuttons Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt | 9 ++++----- .../nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt | 8 ++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 7fd31739a..8853461e6 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -94,13 +94,10 @@ class PollVoteFragment( } binding.pollVoteRadioGroup.setOnCheckedChangeListener { group, checkedId -> - // todo set selected in viewmodel. - Log.d("bb", "click") + viewModel.selectOption(checkedId, true) } - // todo observe viewmodel checked, set view checked with it binding.pollVoteSubmitButton.setOnClickListener { - // viewModel.vote(roomToken, pollId, binding.pollVoteRadioGroup.checkedRadioButtonId) viewModel.vote(roomToken, pollId) } } @@ -116,6 +113,8 @@ class PollVoteFragment( }?.forEachIndexed { index, radioButton -> radioButton.id = index binding.pollVoteRadioGroup.addView(radioButton) + + radioButton.isChecked = viewModel.selectedOptions.contains(index) == true } } else { binding.voteOptionsCheckboxesWrapper.removeAllViews() @@ -128,7 +127,7 @@ class PollVoteFragment( checkBox.isChecked = viewModel.selectedOptions.contains(index) == true checkBox.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) { - viewModel.selectOption(index) + viewModel.selectOption(index, false) } else { viewModel.deSelectOption(index) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 67ffb5fbb..329850090 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -54,8 +54,12 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito _selectedOptions = selectedOptions } - fun selectOption(option: Int) { - _selectedOptions = _selectedOptions.plus(option) + fun selectOption(option: Int, isRadioBox: Boolean) { + if (isRadioBox) { + _selectedOptions = listOf(option) + } else { + _selectedOptions = _selectedOptions.plus(option) + } } fun deSelectOption(option: Int) { From 782d74bfb9e3d62a1457f10d77fada101420430d Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Jun 2022 15:42:59 +0200 Subject: [PATCH 25/92] restrict multiselection to maxVotes Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 8853461e6..9ad98d822 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -28,6 +28,7 @@ import android.view.View import android.view.ViewGroup import android.widget.CheckBox import android.widget.RadioButton +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector @@ -127,7 +128,12 @@ class PollVoteFragment( checkBox.isChecked = viewModel.selectedOptions.contains(index) == true checkBox.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) { - viewModel.selectOption(index, false) + if (poll.maxVotes == UNLIMITED_VOTES || viewModel.selectedOptions.size < poll.maxVotes) { + viewModel.selectOption(index, false) + } else { + checkBox.isChecked = false + Toast.makeText(context, "max votes reached", Toast.LENGTH_LONG).show() + } } else { viewModel.deSelectOption(index) } @@ -154,5 +160,6 @@ class PollVoteFragment( companion object { private val TAG = PollVoteFragment::class.java.simpleName + private const val UNLIMITED_VOTES = 0 } } From a95c0d997b1cb4caae83e782d9a003586ac78275 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Jun 2022 16:01:16 +0200 Subject: [PATCH 26/92] make own votes bold in voting screen Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/ui/PollVoteFragment.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 9ad98d822..ed7b0461b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -21,12 +21,14 @@ package com.nextcloud.talk.polls.ui +import android.graphics.Typeface import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.CheckBox +import android.widget.CompoundButton import android.widget.RadioButton import android.widget.Toast import androidx.fragment.app.Fragment @@ -113,6 +115,10 @@ class PollVoteFragment( RadioButton(context).apply { text = option } }?.forEachIndexed { index, radioButton -> radioButton.id = index + // if (poll.votedSelf?.contains(index) == true) { + // radioButton.setTypeface(null, Typeface.BOLD) + // } + makeOptionBoldIfSelfVoted(radioButton, poll, index) binding.pollVoteRadioGroup.addView(radioButton) radioButton.isChecked = viewModel.selectedOptions.contains(index) == true @@ -123,6 +129,10 @@ class PollVoteFragment( CheckBox(context).apply { text = option } }?.forEachIndexed { index, checkBox -> checkBox.id = index + // if (poll.votedSelf?.contains(index) == true) { + // checkBox.setTypeface(null, Typeface.BOLD) + // } + makeOptionBoldIfSelfVoted(checkBox, poll, index) binding.voteOptionsCheckboxesWrapper.addView(checkBox) checkBox.isChecked = viewModel.selectedOptions.contains(index) == true @@ -142,6 +152,12 @@ class PollVoteFragment( } } + private fun makeOptionBoldIfSelfVoted(button: CompoundButton, poll: Poll, index: Int) { + if (poll.votedSelf?.contains(index) == true) { + button.setTypeface(null, Typeface.BOLD) + } + } + private fun initCloseButton(showCloseButton: Boolean) { if (showCloseButton) { _binding?.pollVoteClosePollButton?.visibility = View.VISIBLE From 167aa43ca070ffda306032b528f50e944e935401 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Jun 2022 12:02:27 +0200 Subject: [PATCH 27/92] make own votes bold in result screen Signed-off-by: Marcel Hibbe --- .../talk/polls/adapters/PollResultItem.kt | 3 ++- .../polls/adapters/PollResultViewHolder.kt | 13 ++++++++++-- .../talk/polls/ui/PollResultsFragment.kt | 21 +++++++++++++++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt index 324253b6d..22d2cda4c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt @@ -2,7 +2,8 @@ package com.nextcloud.talk.polls.adapters class PollResultItem( val pollOption: String, - val pollPercent: Int + val pollPercent: Int, + val selfVoted: Boolean // val voters.... ) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index cee0d95ad..bf0bc409d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -1,20 +1,29 @@ package com.nextcloud.talk.polls.adapters import android.annotation.SuppressLint +import android.graphics.Typeface import android.view.View import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.databinding.PollResultItemBinding class PollResultViewHolder( private val binding: PollResultItemBinding, - private val showDetails: Boolean -) : RecyclerView.ViewHolder(binding.root) { + private val showDetails: Boolean, + + ) : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } + binding.pollOptionText.text = pollResultItem.pollOption binding.pollOptionPercentText.text = pollResultItem.pollPercent.toString() + "%" + + if (pollResultItem.selfVoted) { + binding.pollOptionText.setTypeface(null, Typeface.BOLD) + binding.pollOptionPercentText.setTypeface(null, Typeface.BOLD) + } + binding.pollOptionBar.progress = pollResultItem.pollPercent if (showDetails) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index a40cd0156..8a4964204 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -86,8 +86,8 @@ class PollResultsFragment( } } - private fun initAdapter(showDetails: Boolean) { - adapter = PollResultsAdapter(this, showDetails) + private fun initAdapter(showParticipants: Boolean) { + adapter = PollResultsAdapter(this, showParticipants) _binding?.pollResultsList?.adapter = adapter _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) } @@ -101,7 +101,12 @@ class PollResultsFragment( val votersForThisOption = poll.details.filter { it.optionId == index }.size val optionsPercent = oneVoteInPercent * votersForThisOption - val pollResultItem = PollResultItem(option, optionsPercent) // TODO add participants to PollResultItem + val pollResultItem = PollResultItem( + option, + optionsPercent, + isOptionSelfVoted(poll, index) + ) + // TODO add participants to PollResultItem adapter?.list?.add(pollResultItem) } } else if (poll.votes != null) { @@ -115,7 +120,11 @@ class PollResultsFragment( } val optionsPercent = oneVoteInPercent * votersForThisOption - val pollResultItem = PollResultItem(option, optionsPercent) + val pollResultItem = PollResultItem( + option, + optionsPercent, + isOptionSelfVoted(poll, index) + ) adapter?.list?.add(pollResultItem) } } else { @@ -123,6 +132,10 @@ class PollResultsFragment( } } + private fun isOptionSelfVoted(poll: Poll, index: Int): Boolean { + return poll.votedSelf?.contains(index) == true + } + private fun initEditButton(showEditButton: Boolean) { if (showEditButton) { _binding?.editVoteButton?.visibility = View.VISIBLE From 6447b829697b8a4d2816d6402cca787f6e0fd5a0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Jun 2022 13:19:43 +0200 Subject: [PATCH 28/92] disable vote button when selection is not changed compared to own votes Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollVoteFragment.kt | 23 ++++++++++++------- .../polls/viewmodels/PollVoteViewModel.kt | 7 +++++- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index ed7b0461b..1c4f98ec3 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -78,9 +78,11 @@ class PollVoteFragment( if (state is PollMainViewModel.PollVoteState) { initPollOptions(state.poll) initCloseButton(state.showCloseButton) + updateSubmitButton() } else if (state is PollMainViewModel.PollVoteHiddenState) { initPollOptions(state.poll) initCloseButton(state.showCloseButton) + updateSubmitButton() } } @@ -98,6 +100,7 @@ class PollVoteFragment( binding.pollVoteRadioGroup.setOnCheckedChangeListener { group, checkedId -> viewModel.selectOption(checkedId, true) + updateSubmitButton() } binding.pollVoteSubmitButton.setOnClickListener { @@ -106,8 +109,7 @@ class PollVoteFragment( } private fun initPollOptions(poll: Poll) { - poll.votedSelf?.let { viewModel.initSelectedOptions(it as ArrayList) } - + poll.votedSelf?.let { viewModel.initVotedOptions(it as ArrayList) } if (poll.maxVotes == 1) { binding.pollVoteRadioGroup.removeAllViews() @@ -115,9 +117,6 @@ class PollVoteFragment( RadioButton(context).apply { text = option } }?.forEachIndexed { index, radioButton -> radioButton.id = index - // if (poll.votedSelf?.contains(index) == true) { - // radioButton.setTypeface(null, Typeface.BOLD) - // } makeOptionBoldIfSelfVoted(radioButton, poll, index) binding.pollVoteRadioGroup.addView(radioButton) @@ -129,9 +128,6 @@ class PollVoteFragment( CheckBox(context).apply { text = option } }?.forEachIndexed { index, checkBox -> checkBox.id = index - // if (poll.votedSelf?.contains(index) == true) { - // checkBox.setTypeface(null, Typeface.BOLD) - // } makeOptionBoldIfSelfVoted(checkBox, poll, index) binding.voteOptionsCheckboxesWrapper.addView(checkBox) @@ -147,11 +143,22 @@ class PollVoteFragment( } else { viewModel.deSelectOption(index) } + updateSubmitButton() } } } } + private fun updateSubmitButton() { + binding.pollVoteSubmitButton.isEnabled = + areSelectedOptionsDifferentToVotedOptions() && viewModel.selectedOptions.isNotEmpty() + } + + private fun areSelectedOptionsDifferentToVotedOptions(): Boolean { + return !(viewModel.votedOptions.containsAll(viewModel.selectedOptions) && + viewModel.selectedOptions.containsAll(viewModel.votedOptions)) + } + private fun makeOptionBoldIfSelfVoted(button: CompoundButton, poll: Poll, index: Int) { if (poll.votedSelf?.contains(index) == true) { button.setTypeface(null, Typeface.BOLD) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 329850090..891db9515 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -46,11 +46,16 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito private var disposable: Disposable? = null + private var _votedOptions: List = emptyList() + val votedOptions: List + get() = _votedOptions + private var _selectedOptions: List = emptyList() val selectedOptions: List get() = _selectedOptions - fun initSelectedOptions(selectedOptions: List) { + fun initVotedOptions(selectedOptions: List) { + _votedOptions = selectedOptions _selectedOptions = selectedOptions } From fb54b35cf06adf5398c7ae5eee73d4770ce19fe6 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Jun 2022 13:56:29 +0200 Subject: [PATCH 29/92] rename close poll to end poll Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_poll_results.xml | 2 +- app/src/main/res/layout/dialog_poll_vote.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 06443366b..89d435994 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -41,7 +41,7 @@ android:id="@+id/poll_results_close_poll_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/polls_close_poll" + android:text="@string/polls_end_poll" style="@style/OutlinedButton" android:layout_marginEnd="@dimen/standard_margin" app:cornerRadius="@dimen/button_corner_radius" diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 407d9a43f..787d91cbf 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -54,7 +54,7 @@ android:id="@+id/poll_vote_close_poll_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/polls_close_poll" + android:text="@string/polls_end_poll" style="@style/OutlinedButton" android:layout_marginEnd="@dimen/standard_margin" app:cornerRadius="@dimen/button_corner_radius" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 644436c16..1521abeaa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -539,7 +539,7 @@ Poll results - %1$s votes Add Option - Close Poll + End Poll Attachments From 318549a63a8cc9c881be784d45cd1297191db0d2 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Jun 2022 23:13:40 +0200 Subject: [PATCH 30/92] wip: show user avatars for closed public polls Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 1 + .../OutcomingPollMessageViewHolder.kt | 1 + .../talk/polls/adapters/PollResultItem.kt | 11 +-- .../polls/adapters/PollResultViewHolder.kt | 88 +++++++++++++++++-- .../talk/polls/adapters/PollResultsAdapter.kt | 8 +- .../talk/polls/ui/PollMainDialogFragment.kt | 6 +- .../talk/polls/ui/PollResultsFragment.kt | 27 +++--- .../polls/viewmodels/PollMainViewModel.kt | 4 +- .../main/res/layout/dialog_poll_results.xml | 2 +- app/src/main/res/layout/poll_result_item.xml | 5 -- 10 files changed, 115 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 8198748a0..51bab9f80 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -125,6 +125,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH binding.bubble.setOnClickListener { val pollVoteDialog = PollMainDialogFragment.newInstance( + message.activeUser!!, roomToken, pollId, pollName diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index ccf339b64..15b3d8ab0 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -141,6 +141,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag binding.bubble.setOnClickListener { val pollVoteDialog = PollMainDialogFragment.newInstance( + message.activeUser!!, roomToken, pollId, pollName diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt index 22d2cda4c..19051ea61 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt @@ -1,9 +1,10 @@ package com.nextcloud.talk.polls.adapters -class PollResultItem( - val pollOption: String, - val pollPercent: Int, - val selfVoted: Boolean +import com.nextcloud.talk.polls.model.PollDetails - // val voters.... +class PollResultItem( + val name: String, + val percent: Int, + val selfVoted: Boolean, + val voters: List? ) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index bf0bc409d..b8f400340 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -2,34 +2,106 @@ package com.nextcloud.talk.polls.adapters import android.annotation.SuppressLint import android.graphics.Typeface +import android.text.TextUtils import android.view.View +import android.widget.LinearLayout import androidx.recyclerview.widget.RecyclerView +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.generic.RoundingParams +import com.facebook.drawee.interfaces.DraweeController +import com.facebook.drawee.view.SimpleDraweeView +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.PollResultItemBinding +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.polls.model.PollDetails +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DisplayUtils class PollResultViewHolder( - private val binding: PollResultItemBinding, - private val showDetails: Boolean, - - ) : RecyclerView.ViewHolder(binding.root) { + private val user: UserEntity, + private val binding: PollResultItemBinding +) : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } - binding.pollOptionText.text = pollResultItem.pollOption - binding.pollOptionPercentText.text = pollResultItem.pollPercent.toString() + "%" + // binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } + + binding.pollOptionText.text = pollResultItem.name + binding.pollOptionPercentText.text = "${pollResultItem.percent}%" if (pollResultItem.selfVoted) { binding.pollOptionText.setTypeface(null, Typeface.BOLD) binding.pollOptionPercentText.setTypeface(null, Typeface.BOLD) } - binding.pollOptionBar.progress = pollResultItem.pollPercent + binding.pollOptionBar.progress = pollResultItem.percent - if (showDetails) { + if (!pollResultItem.voters.isNullOrEmpty()) { binding.pollOptionDetail.visibility = View.VISIBLE + + val lp = LinearLayout.LayoutParams( + 90, + 70 + ) + + pollResultItem.voters.forEach { + val avatar = SimpleDraweeView(binding.root.context) + avatar.layoutParams = lp + + val roundingParams = RoundingParams.fromCornersRadius(5f) + roundingParams.roundAsCircle = true + + avatar.hierarchy.roundingParams = roundingParams + avatar.controller = getAvatarDraweeController(it) + + binding.pollOptionDetail.addView(avatar) + } } else { binding.pollOptionDetail.visibility = View.GONE } } + + private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? { + if (pollDetail.actorType == "guests") { + var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest) + if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { + displayName = pollDetail.actorDisplayName!! + } + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + // .setOldController(binding.avatar.controller) + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForGuestAvatar( + user.baseUrl, + displayName, + false + ), + null + ) + ) + .build() + return draweeController + } else if (pollDetail.actorType == "users") { + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + // .setOldController(binding.avatar.controller) + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatar( + user.baseUrl, + pollDetail.actorId, + false + ), + null + ) + ) + .build() + return draweeController + } + return null + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index 039ec4900..63827661b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -4,20 +4,22 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.databinding.PollResultItemBinding +import com.nextcloud.talk.models.database.UserEntity class PollResultsAdapter( + private val user: UserEntity, private val clickListener: PollResultItemClickListener, - private val showDetails: Boolean ) : RecyclerView.Adapter() { internal var list: MutableList = ArrayList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollResultViewHolder { val itemBinding = PollResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return PollResultViewHolder(itemBinding, showDetails) + return PollResultViewHolder(user, itemBinding) } override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) { - holder.bind(list[position], clickListener) + val pollResultItem = list[position] + holder.bind(pollResultItem, clickListener) } override fun getItemCount(): Int { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index ba7f0d57d..88859b881 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -13,12 +13,14 @@ import autodagger.AutoInjector import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollMainBinding +import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollMainDialogFragment( + private val user: UserEntity, private val pollId: String, private val roomToken: String, private val pollTitle: String @@ -91,6 +93,7 @@ class PollMainDialogFragment( initVotersAmount(poll.numVoters) val contentFragment = PollResultsFragment( + user, viewModel, roomToken, pollId @@ -114,9 +117,10 @@ class PollMainDialogFragment( companion object { @JvmStatic fun newInstance( + user: UserEntity, roomTokenParam: String, pollId: String, name: String - ): PollMainDialogFragment = PollMainDialogFragment(pollId, roomTokenParam, name) + ): PollMainDialogFragment = PollMainDialogFragment(user, pollId, roomTokenParam, name) } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 8a4964204..85dbcb5e3 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -32,6 +32,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollResultsBinding +import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.polls.adapters.PollResultItem import com.nextcloud.talk.polls.adapters.PollResultItemClickListener import com.nextcloud.talk.polls.adapters.PollResultsAdapter @@ -42,6 +43,7 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollResultsFragment( + private val user: UserEntity, private val parentViewModel: PollMainViewModel, private val roomToken: String, private val pollId: String @@ -78,7 +80,7 @@ class PollResultsFragment( parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollResultState) { - initAdapter(state.showParticipants) + initAdapter() initPollResults(state.poll) initEditButton(state.showEditButton) initCloseButton(state.showCloseButton) @@ -86,8 +88,8 @@ class PollResultsFragment( } } - private fun initAdapter(showParticipants: Boolean) { - adapter = PollResultsAdapter(this, showParticipants) + private fun initAdapter() { + adapter = PollResultsAdapter(user, this) _binding?.pollResultsList?.adapter = adapter _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) } @@ -98,15 +100,15 @@ class PollResultsFragment( val oneVoteInPercent = 100 / votersAmount // TODO: poll.numVoters when fixed on api poll.options?.forEachIndexed { index, option -> - val votersForThisOption = poll.details.filter { it.optionId == index }.size - val optionsPercent = oneVoteInPercent * votersForThisOption + val votersForThisOption = poll.details.filter { it.optionId == index } + val optionsPercent = oneVoteInPercent * votersForThisOption.size val pollResultItem = PollResultItem( option, optionsPercent, - isOptionSelfVoted(poll, index) + isOptionSelfVoted(poll, index), + votersForThisOption ) - // TODO add participants to PollResultItem adapter?.list?.add(pollResultItem) } } else if (poll.votes != null) { @@ -114,16 +116,17 @@ class PollResultsFragment( val oneVoteInPercent = 100 / votersAmount poll.options?.forEachIndexed { index, option -> - var votersForThisOption = poll.votes.filter { it.key.toInt() == index }[index.toString()] - if (votersForThisOption == null) { - votersForThisOption = 0 + var votersAmountForThisOption = poll.votes.filter { it.key.toInt() == index }[index.toString()] + if (votersAmountForThisOption == null) { + votersAmountForThisOption = 0 } - val optionsPercent = oneVoteInPercent * votersForThisOption + val optionsPercent = oneVoteInPercent * votersAmountForThisOption val pollResultItem = PollResultItem( option, optionsPercent, - isOptionSelfVoted(poll, index) + isOptionSelfVoted(poll, index), + null ) adapter?.list?.add(pollResultItem) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 909f108a6..e9f66f242 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -48,7 +48,6 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito open class PollResultState( val poll: Poll, - val showParticipants: Boolean, val showEditButton: Boolean, val showCloseButton: Boolean ) : ViewState @@ -129,11 +128,10 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito } private fun setPollResultState(poll: Poll) { - val showDetails = poll.status == Poll.STATUS_CLOSED && poll.resultMode == Poll.RESULT_MODE_PUBLIC val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) - _viewState.value = PollResultState(poll, showDetails, showEditButton, showCloseButton) + _viewState.value = PollResultState(poll, showEditButton, showCloseButton) } private fun votedForOpenHiddenPoll(poll: Poll): Boolean { diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 89d435994..8b7df33de 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -26,7 +26,7 @@ diff --git a/app/src/main/res/layout/poll_result_item.xml b/app/src/main/res/layout/poll_result_item.xml index d04974b62..73850adb2 100644 --- a/app/src/main/res/layout/poll_result_item.xml +++ b/app/src/main/res/layout/poll_result_item.xml @@ -38,11 +38,6 @@ app:layout_constraintStart_toStartOf="@+id/poll_option_text" app:layout_constraintTop_toBottomOf="@+id/poll_option_bar"> - - From bddd6b2e8ec5ac33e439e4ba482399b249693693 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Jun 2022 23:57:06 +0200 Subject: [PATCH 31/92] wip: styling of poll bars Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/poll_result_item.xml | 23 +++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/layout/poll_result_item.xml b/app/src/main/res/layout/poll_result_item.xml index 73850adb2..cb8bfb47e 100644 --- a/app/src/main/res/layout/poll_result_item.xml +++ b/app/src/main/res/layout/poll_result_item.xml @@ -3,13 +3,13 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingBottom="@dimen/standard_padding" tools:background="@color/white"> @@ -19,25 +19,32 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:text="25 %" /> + app:layout_constraintTop_toTopOf="@+id/poll_option_text" + tools:text="50%" /> - + app:trackColor="@color/dialog_background" + app:trackCornerRadius="5dp" + app:trackThickness="10dp" + tools:progress="50" /> - - + app:layout_constraintTop_toBottomOf="@+id/poll_option_bar" /> From 909ee07ce6bb34b9647c6cacd2e66a6823178d9d Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 29 Jun 2022 12:28:08 +0200 Subject: [PATCH 32/92] wip2: styling of poll bars Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/adapters/PollResultViewHolder.kt | 6 +++--- app/src/main/res/layout/poll_result_item.xml | 4 ++-- app/src/main/res/values/colors.xml | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index b8f400340..dab8c90bd 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -27,7 +27,7 @@ class PollResultViewHolder( fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } - // binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } + binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } binding.pollOptionText.text = pollResultItem.name binding.pollOptionPercentText.text = "${pollResultItem.percent}%" @@ -43,8 +43,8 @@ class PollResultViewHolder( binding.pollOptionDetail.visibility = View.VISIBLE val lp = LinearLayout.LayoutParams( - 90, - 70 + 60, + 50 ) pollResultItem.voters.forEach { diff --git a/app/src/main/res/layout/poll_result_item.xml b/app/src/main/res/layout/poll_result_item.xml index cb8bfb47e..9e2852d3f 100644 --- a/app/src/main/res/layout/poll_result_item.xml +++ b/app/src/main/res/layout/poll_result_item.xml @@ -28,12 +28,12 @@ android:layout_height="wrap_content" android:layout_marginTop="4dp" android:indeterminate="false" - app:indicatorColor="@color/colorPrimary" + app:indicatorColor="@color/poll_bar_color" app:layout_constraintStart_toStartOf="@+id/poll_option_text" app:layout_constraintTop_toBottomOf="@+id/poll_option_text" app:trackColor="@color/dialog_background" app:trackCornerRadius="5dp" - app:trackThickness="10dp" + app:trackThickness="5dp" tools:progress="50" /> #eeeeee #EEEEEE + + #8dd4f6 + #FFFFFF From 73d48a395cd539381bca6d57e71978376a5b8935 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 29 Jun 2022 15:17:50 +0200 Subject: [PATCH 33/92] show detailed list of voters + refactoring adapter and viewholders for result screen Signed-off-by: Marcel Hibbe --- .../polls/adapters/PollResultHeaderItem.kt | 19 ++++ .../adapters/PollResultHeaderViewHolder.kt | 29 +++++ .../talk/polls/adapters/PollResultItem.kt | 11 +- .../adapters/PollResultItemClickListener.kt | 2 +- .../polls/adapters/PollResultViewHolder.kt | 107 +----------------- .../polls/adapters/PollResultVoterItem.kt | 18 +++ .../adapters/PollResultVoterViewHolder.kt | 84 ++++++++++++++ .../talk/polls/adapters/PollResultsAdapter.kt | 42 ++++++- .../talk/polls/ui/PollResultsFragment.kt | 62 ++-------- .../polls/viewmodels/PollResultsViewModel.kt | 90 +++++++++++++++ .../main/res/layout/dialog_poll_results.xml | 2 +- ...t_item.xml => poll_result_header_item.xml} | 12 +- .../res/layout/poll_result_voter_item.xml | 25 ++++ 13 files changed, 324 insertions(+), 179 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt rename app/src/main/res/layout/{poll_result_item.xml => poll_result_header_item.xml} (77%) create mode 100644 app/src/main/res/layout/poll_result_voter_item.xml diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt new file mode 100644 index 000000000..a0dd6bfd3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt @@ -0,0 +1,19 @@ +package com.nextcloud.talk.polls.adapters + +import com.nextcloud.talk.R + +data class PollResultHeaderItem( + val name: String, + val percent: Int, + val selfVoted: Boolean +) : PollResultItem { + + override fun getViewType(): Int { + return VIEW_TYPE + } + + companion object { + // layout is used as view type for uniqueness + public val VIEW_TYPE: Int = R.layout.poll_result_header_item + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt new file mode 100644 index 000000000..0c73ac55e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt @@ -0,0 +1,29 @@ +package com.nextcloud.talk.polls.adapters + +import android.annotation.SuppressLint +import android.graphics.Typeface +import com.nextcloud.talk.databinding.PollResultHeaderItemBinding +import com.nextcloud.talk.models.database.UserEntity + +class PollResultHeaderViewHolder( + private val user: UserEntity, + override val binding: PollResultHeaderItemBinding +) : PollResultViewHolder(binding) { + + @SuppressLint("SetTextI18n") + override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { + val item = pollResultItem as PollResultHeaderItem + + binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } + + binding.pollOptionText.text = item.name + binding.pollOptionPercentText.text = "${item.percent}%" + + if (item.selfVoted) { + binding.pollOptionText.setTypeface(null, Typeface.BOLD) + binding.pollOptionPercentText.setTypeface(null, Typeface.BOLD) + } + + binding.pollOptionBar.progress = item.percent + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt index 19051ea61..8349c2886 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt @@ -1,10 +1,7 @@ package com.nextcloud.talk.polls.adapters -import com.nextcloud.talk.polls.model.PollDetails +interface PollResultItem { -class PollResultItem( - val name: String, - val percent: Int, - val selfVoted: Boolean, - val voters: List? -) + fun getViewType(): Int + // fun getView(inflater: LayoutInflater?, convertView: View?): View? +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt index 0f73a37a2..9da748950 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt @@ -1,5 +1,5 @@ package com.nextcloud.talk.polls.adapters interface PollResultItemClickListener { - fun onClick(pollResultItem: PollResultItem) + fun onClick(pollResultHeaderItem: PollResultHeaderItem) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index dab8c90bd..5eda74cce 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -1,107 +1,10 @@ package com.nextcloud.talk.polls.adapters -import android.annotation.SuppressLint -import android.graphics.Typeface -import android.text.TextUtils -import android.view.View -import android.widget.LinearLayout import androidx.recyclerview.widget.RecyclerView -import com.facebook.drawee.backends.pipeline.Fresco -import com.facebook.drawee.generic.RoundingParams -import com.facebook.drawee.interfaces.DraweeController -import com.facebook.drawee.view.SimpleDraweeView -import com.nextcloud.talk.R -import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.databinding.PollResultItemBinding -import com.nextcloud.talk.models.database.UserEntity -import com.nextcloud.talk.polls.model.PollDetails -import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.DisplayUtils +import androidx.viewbinding.ViewBinding -class PollResultViewHolder( - private val user: UserEntity, - private val binding: PollResultItemBinding +abstract class PollResultViewHolder( + open val binding: ViewBinding ) : RecyclerView.ViewHolder(binding.root) { - - @SuppressLint("SetTextI18n") - fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { - binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } - - binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } - - binding.pollOptionText.text = pollResultItem.name - binding.pollOptionPercentText.text = "${pollResultItem.percent}%" - - if (pollResultItem.selfVoted) { - binding.pollOptionText.setTypeface(null, Typeface.BOLD) - binding.pollOptionPercentText.setTypeface(null, Typeface.BOLD) - } - - binding.pollOptionBar.progress = pollResultItem.percent - - if (!pollResultItem.voters.isNullOrEmpty()) { - binding.pollOptionDetail.visibility = View.VISIBLE - - val lp = LinearLayout.LayoutParams( - 60, - 50 - ) - - pollResultItem.voters.forEach { - val avatar = SimpleDraweeView(binding.root.context) - avatar.layoutParams = lp - - val roundingParams = RoundingParams.fromCornersRadius(5f) - roundingParams.roundAsCircle = true - - avatar.hierarchy.roundingParams = roundingParams - avatar.controller = getAvatarDraweeController(it) - - binding.pollOptionDetail.addView(avatar) - } - } else { - binding.pollOptionDetail.visibility = View.GONE - } - } - - private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? { - if (pollDetail.actorType == "guests") { - var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest) - if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { - displayName = pollDetail.actorDisplayName!! - } - val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() - // .setOldController(binding.avatar.controller) - .setAutoPlayAnimations(true) - .setImageRequest( - DisplayUtils.getImageRequestForUrl( - ApiUtils.getUrlForGuestAvatar( - user.baseUrl, - displayName, - false - ), - null - ) - ) - .build() - return draweeController - } else if (pollDetail.actorType == "users") { - val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() - // .setOldController(binding.avatar.controller) - .setAutoPlayAnimations(true) - .setImageRequest( - DisplayUtils.getImageRequestForUrl( - ApiUtils.getUrlForAvatar( - user.baseUrl, - pollDetail.actorId, - false - ), - null - ) - ) - .build() - return draweeController - } - return null - } -} + abstract fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt new file mode 100644 index 000000000..e05943f09 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt @@ -0,0 +1,18 @@ +package com.nextcloud.talk.polls.adapters + +import com.nextcloud.talk.R +import com.nextcloud.talk.polls.model.PollDetails + +data class PollResultVoterItem( + val details: PollDetails +) : PollResultItem { + + override fun getViewType(): Int { + return VIEW_TYPE + } + + companion object { + // layout is used as view type for uniqueness + public val VIEW_TYPE: Int = R.layout.poll_result_voter_item + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt new file mode 100644 index 000000000..1e105e216 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt @@ -0,0 +1,84 @@ +package com.nextcloud.talk.polls.adapters + +import android.annotation.SuppressLint +import android.text.TextUtils +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.databinding.PollResultVoterItemBinding +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.polls.model.PollDetails +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DisplayUtils + +class PollResultVoterViewHolder( + private val user: UserEntity, + override val binding: PollResultVoterItemBinding +) : PollResultViewHolder(binding) { + + @SuppressLint("SetTextI18n") + override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { + val item = pollResultItem as PollResultVoterItem + + // binding.root.setOnClickListener { clickListener.onClick(pollResultVoterItem) } + + // binding.pollVoterAvatar = pollResultHeaderItem.name + binding.pollVoterName.text = item.details.actorDisplayName + + // val lp = LinearLayout.LayoutParams( + // 60, + // 50 + // ) + // + // val avatar = SimpleDraweeView(binding.root.context) + // avatar.layoutParams = lp + + // val roundingParams = RoundingParams.fromCornersRadius(5f) + // roundingParams.roundAsCircle = true + // + // binding.pollVoterAvatar.hierarchy.roundingParams = roundingParams + binding.pollVoterAvatar.controller = getAvatarDraweeController(item.details) + } + + private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? { + if (pollDetail.actorType == "guests") { + var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest) + if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { + displayName = pollDetail.actorDisplayName!! + } + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + // .setOldController(binding.avatar.controller) + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForGuestAvatar( + user.baseUrl, + displayName, + false + ), + null + ) + ) + .build() + return draweeController + } else if (pollDetail.actorType == "users") { + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + // .setOldController(binding.avatar.controller) + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatar( + user.baseUrl, + pollDetail.actorId, + false + ), + null + ) + ) + .build() + return draweeController + } + return null + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index 63827661b..153a198e5 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -3,26 +3,56 @@ package com.nextcloud.talk.polls.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.nextcloud.talk.databinding.PollResultItemBinding +import com.nextcloud.talk.databinding.PollResultHeaderItemBinding +import com.nextcloud.talk.databinding.PollResultVoterItemBinding import com.nextcloud.talk.models.database.UserEntity class PollResultsAdapter( private val user: UserEntity, private val clickListener: PollResultItemClickListener, ) : RecyclerView.Adapter() { - internal var list: MutableList = ArrayList() + internal var list: MutableList = ArrayList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollResultViewHolder { - val itemBinding = PollResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return PollResultViewHolder(user, itemBinding) + when (viewType) { + PollResultHeaderItem.VIEW_TYPE -> { + val itemBinding = PollResultHeaderItemBinding.inflate( + LayoutInflater.from(parent.context), parent, + false + ) + return PollResultHeaderViewHolder(user, itemBinding) + } + PollResultVoterItem.VIEW_TYPE -> { + val itemBinding = PollResultVoterItemBinding.inflate( + LayoutInflater.from(parent.context), parent, + false + ) + return PollResultVoterViewHolder(user, itemBinding) + } + } + + val itemBinding = PollResultHeaderItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return PollResultHeaderViewHolder(user, itemBinding) } override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) { - val pollResultItem = list[position] - holder.bind(pollResultItem, clickListener) + when (holder.itemViewType) { + PollResultHeaderItem.VIEW_TYPE -> { + val pollResultItem = list[position] + holder.bind(pollResultItem as PollResultHeaderItem, clickListener) + } + PollResultVoterItem.VIEW_TYPE -> { + val pollResultItem = list[position] + holder.bind(pollResultItem as PollResultVoterItem, clickListener) + } + } } override fun getItemCount(): Int { return list.size } + + override fun getItemViewType(position: Int): Int { + return list[position].getViewType() + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 85dbcb5e3..f67fa958e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -22,7 +22,6 @@ package com.nextcloud.talk.polls.ui import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -33,10 +32,9 @@ import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollResultsBinding import com.nextcloud.talk.models.database.UserEntity -import com.nextcloud.talk.polls.adapters.PollResultItem +import com.nextcloud.talk.polls.adapters.PollResultHeaderItem import com.nextcloud.talk.polls.adapters.PollResultItemClickListener import com.nextcloud.talk.polls.adapters.PollResultsAdapter -import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel import javax.inject.Inject @@ -81,11 +79,18 @@ class PollResultsFragment( parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollResultState) { initAdapter() - initPollResults(state.poll) + viewModel.setPoll(state.poll) initEditButton(state.showEditButton) initCloseButton(state.showCloseButton) } } + + viewModel.items.observe(viewLifecycleOwner) { + val adapter = PollResultsAdapter(user, this).apply { + list = it + } + binding.pollResultsList.adapter = adapter + } } private fun initAdapter() { @@ -94,51 +99,6 @@ class PollResultsFragment( _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) } - private fun initPollResults(poll: Poll) { - if (poll.details != null) { - val votersAmount = poll.details.size - val oneVoteInPercent = 100 / votersAmount // TODO: poll.numVoters when fixed on api - - poll.options?.forEachIndexed { index, option -> - val votersForThisOption = poll.details.filter { it.optionId == index } - val optionsPercent = oneVoteInPercent * votersForThisOption.size - - val pollResultItem = PollResultItem( - option, - optionsPercent, - isOptionSelfVoted(poll, index), - votersForThisOption - ) - adapter?.list?.add(pollResultItem) - } - } else if (poll.votes != null) { - val votersAmount = poll.numVoters - val oneVoteInPercent = 100 / votersAmount - - poll.options?.forEachIndexed { index, option -> - var votersAmountForThisOption = poll.votes.filter { it.key.toInt() == index }[index.toString()] - if (votersAmountForThisOption == null) { - votersAmountForThisOption = 0 - } - val optionsPercent = oneVoteInPercent * votersAmountForThisOption - - val pollResultItem = PollResultItem( - option, - optionsPercent, - isOptionSelfVoted(poll, index), - null - ) - adapter?.list?.add(pollResultItem) - } - } else { - Log.e(TAG, "failed to get data to show poll results") - } - } - - private fun isOptionSelfVoted(poll: Poll, index: Int): Boolean { - return poll.votedSelf?.contains(index) == true - } - private fun initEditButton(showEditButton: Boolean) { if (showEditButton) { _binding?.editVoteButton?.visibility = View.VISIBLE @@ -161,8 +121,8 @@ class PollResultsFragment( } } - override fun onClick(pollResultItem: PollResultItem) { - Log.d(TAG, "click..") + override fun onClick(pollResultHeaderItem: PollResultHeaderItem) { + viewModel.filterItems() } override fun onDestroyView() { diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 63bf30339..869fdbbf3 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -24,6 +24,10 @@ package com.nextcloud.talk.polls.viewmodels import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.nextcloud.talk.polls.adapters.PollResultHeaderItem +import com.nextcloud.talk.polls.adapters.PollResultItem +import com.nextcloud.talk.polls.adapters.PollResultVoterItem +import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.repositories.PollRepository import io.reactivex.disposables.Disposable import javax.inject.Inject @@ -33,10 +37,20 @@ class PollResultsViewModel @Inject constructor(private val repository: PollRepos sealed interface ViewState object InitialState : ViewState + private var _poll: Poll? = null + val poll: Poll? + get() = _poll + private val _viewState: MutableLiveData = MutableLiveData(InitialState) val viewState: LiveData get() = _viewState + private var _unfilteredItems: ArrayList = ArrayList() + + private var _items: MutableLiveData> = MutableLiveData>() + val items: LiveData> + get() = _items + private var disposable: Disposable? = null override fun onCleared() { @@ -44,6 +58,82 @@ class PollResultsViewModel @Inject constructor(private val repository: PollRepos disposable?.dispose() } + fun setPoll(poll: Poll) { + _poll = poll + initPollResults(_poll!!) + } + + private fun initPollResults(poll: Poll) { + _items.value = ArrayList() + + val votersAmount = getVotersAmount(poll) + val oneVoteInPercent = 100 / votersAmount + + poll.options?.forEachIndexed { index, option -> + val votersAmountForThisOption = getVotersAmountForOption(poll, index) + val optionsPercent = oneVoteInPercent * votersAmountForThisOption + + val pollResultHeaderItem = PollResultHeaderItem( + option, + optionsPercent, + isOptionSelfVoted(poll, index) + ) + addToItems(pollResultHeaderItem) + + val voters = poll.details?.filter { it.optionId == index } + if (!voters.isNullOrEmpty()) { + voters.forEach { + addToItems(PollResultVoterItem(it)) + } + } + } + + _unfilteredItems = _items.value?.let { ArrayList(it) }!! + } + + private fun addToItems(pollResultItem: PollResultItem) { + val tempList = _items.value + tempList!!.add(pollResultItem) + _items.value = tempList + } + + private fun getVotersAmount(poll: Poll): Int { + if (poll.details != null) { + return poll.details.size + } else if (poll.votes != null) { + return poll.numVoters + } + return 0 + } + + private fun getVotersAmountForOption(poll: Poll, index: Int): Int { + var votersAmountForThisOption: Int? = 0 + if (poll.details != null) { + votersAmountForThisOption = poll.details.filter { it.optionId == index }.size + } else if (poll.votes != null) { + votersAmountForThisOption = poll.votes.filter { it.key.toInt() == index }[index.toString()] + if (votersAmountForThisOption == null) { + votersAmountForThisOption = 0 + } + } + return votersAmountForThisOption!! + } + + private fun isOptionSelfVoted(poll: Poll, index: Int): Boolean { + return poll.votedSelf?.contains(index) == true + } + + fun filterItems() { + if (_items.value?.containsAll(_unfilteredItems) == true) { + val filteredList = _items.value?.filter { it.getViewType() == PollResultHeaderItem.VIEW_TYPE } as + MutableList + + _items.value = ArrayList(filteredList) + } else { + _items.value = _unfilteredItems + } + } + companion object { private val TAG = PollResultsViewModel::class.java.simpleName } diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 8b7df33de..5fc62f79e 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -34,7 +34,7 @@ android:id="@+id/poll_results_list" android:layout_width="match_parent" android:layout_height="wrap_content" - tools:listitem="@layout/poll_result_item" /> + tools:listitem="@layout/poll_result_header_item" /> - - diff --git a/app/src/main/res/layout/poll_result_voter_item.xml b/app/src/main/res/layout/poll_result_voter_item.xml new file mode 100644 index 000000000..8039f06bf --- /dev/null +++ b/app/src/main/res/layout/poll_result_voter_item.xml @@ -0,0 +1,25 @@ + + + + + + + From 01f7016ae2c3caeb07f9a59d583ba07d265a5ee8 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 30 Jun 2022 10:03:15 +0200 Subject: [PATCH 34/92] convert "votes" map see https://github.com/nextcloud/spreed/pull/7500 Signed-off-by: Marcel Hibbe --- .../talk/polls/repositories/PollRepositoryImpl.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index d5329869a..60939718c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -123,7 +123,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid pollResponse.id, pollResponse.question, pollResponse.options, - pollResponse.votes, + convertVotes(pollResponse.votes), pollResponse.actorType, pollResponse.actorId, pollResponse.actorDisplayName, @@ -137,6 +137,14 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return poll } + private fun convertVotes(votes: Map?): Map { + val resultMap: MutableMap = HashMap() + votes?.forEach { + resultMap[it.key.replace("option-", "")] = it.value + } + return resultMap + } + private fun mapToPollDetails(pollDetailsResponse: PollDetailsResponse): PollDetails { return PollDetails( pollDetailsResponse.actorType, From bd99245c268160fe519e7b008b4dd2b7cc417a3e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 30 Jun 2022 12:42:27 +0200 Subject: [PATCH 35/92] add confirm dialog to end poll Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollResultsFragment.kt | 13 ++++++++++++- .../talk/polls/ui/PollVoteFragment.kt | 18 ++++++++++++++---- app/src/main/res/layout/dialog_poll_vote.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index f67fa958e..ea45392cc 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -21,14 +21,17 @@ package com.nextcloud.talk.polls.ui +import android.content.DialogInterface import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector +import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollResultsBinding import com.nextcloud.talk.models.database.UserEntity @@ -114,7 +117,15 @@ class PollResultsFragment( if (showCloseButton) { _binding?.pollResultsClosePollButton?.visibility = View.VISIBLE _binding?.pollResultsClosePollButton?.setOnClickListener { - parentViewModel.closePoll() + AlertDialog.Builder(requireContext()) + .setTitle(R.string.polls_end_poll) + .setMessage(R.string.polls_end_poll_confirm) + .setPositiveButton(R.string.polls_end_poll, DialogInterface.OnClickListener { _, _ -> + parentViewModel.closePoll() + }) + .setNegativeButton(R.string.nc_cancel, null) + .show() + } } else { _binding?.pollResultsClosePollButton?.visibility = View.GONE diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 1c4f98ec3..e01f4b497 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -21,6 +21,7 @@ package com.nextcloud.talk.polls.ui +import android.content.DialogInterface import android.graphics.Typeface import android.os.Bundle import android.util.Log @@ -31,9 +32,11 @@ import android.widget.CheckBox import android.widget.CompoundButton import android.widget.RadioButton import android.widget.Toast +import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector +import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollVoteBinding import com.nextcloud.talk.polls.model.Poll @@ -167,12 +170,19 @@ class PollVoteFragment( private fun initCloseButton(showCloseButton: Boolean) { if (showCloseButton) { - _binding?.pollVoteClosePollButton?.visibility = View.VISIBLE - _binding?.pollVoteClosePollButton?.setOnClickListener { - parentViewModel.closePoll() + _binding?.pollVoteEndPollButton?.visibility = View.VISIBLE + _binding?.pollVoteEndPollButton?.setOnClickListener { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.polls_end_poll) + .setMessage(R.string.polls_end_poll_confirm) + .setPositiveButton(R.string.polls_end_poll, DialogInterface.OnClickListener { _, _ -> + parentViewModel.closePoll() + }) + .setNegativeButton(R.string.nc_cancel, null) + .show() } } else { - _binding?.pollVoteClosePollButton?.visibility = View.GONE + _binding?.pollVoteEndPollButton?.visibility = View.GONE } } diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 787d91cbf..c6f6c5e7c 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -51,7 +51,7 @@ Poll results - %1$s votes Add Option End Poll + Do you really want to end this poll? This can\'t be undone. Attachments From b347fbf1b5a02b7245a651874905d8b574b7780c Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 30 Jun 2022 13:23:48 +0200 Subject: [PATCH 36/92] rename close poll to end poll (in code) Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/ui/PollResultsFragment.kt | 13 ++++++------- .../com/nextcloud/talk/polls/ui/PollVoteFragment.kt | 8 ++++---- .../talk/polls/viewmodels/PollMainViewModel.kt | 6 +++--- app/src/main/res/layout/dialog_poll_results.xml | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index ea45392cc..fa97a6b25 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -84,7 +84,7 @@ class PollResultsFragment( initAdapter() viewModel.setPoll(state.poll) initEditButton(state.showEditButton) - initCloseButton(state.showCloseButton) + initEndPollButton(state.showEndPollButton) } } @@ -113,10 +113,10 @@ class PollResultsFragment( } } - private fun initCloseButton(showCloseButton: Boolean) { - if (showCloseButton) { - _binding?.pollResultsClosePollButton?.visibility = View.VISIBLE - _binding?.pollResultsClosePollButton?.setOnClickListener { + private fun initEndPollButton(showEndPollButton: Boolean) { + if (showEndPollButton) { + _binding?.pollResultsEndPollButton?.visibility = View.VISIBLE + _binding?.pollResultsEndPollButton?.setOnClickListener { AlertDialog.Builder(requireContext()) .setTitle(R.string.polls_end_poll) .setMessage(R.string.polls_end_poll_confirm) @@ -125,10 +125,9 @@ class PollResultsFragment( }) .setNegativeButton(R.string.nc_cancel, null) .show() - } } else { - _binding?.pollResultsClosePollButton?.visibility = View.GONE + _binding?.pollResultsEndPollButton?.visibility = View.GONE } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index e01f4b497..eb805889e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -80,11 +80,11 @@ class PollVoteFragment( parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollVoteState) { initPollOptions(state.poll) - initCloseButton(state.showCloseButton) + initEndPollButton(state.showEndPollButton) updateSubmitButton() } else if (state is PollMainViewModel.PollVoteHiddenState) { initPollOptions(state.poll) - initCloseButton(state.showCloseButton) + initEndPollButton(state.showEndPollButton) updateSubmitButton() } } @@ -168,8 +168,8 @@ class PollVoteFragment( } } - private fun initCloseButton(showCloseButton: Boolean) { - if (showCloseButton) { + private fun initEndPollButton(showEndPollButton: Boolean) { + if (showEndPollButton) { _binding?.pollVoteEndPollButton?.visibility = View.VISIBLE _binding?.pollVoteEndPollButton?.setOnClickListener { AlertDialog.Builder(requireContext()) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index e9f66f242..2c3d35ee1 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -38,18 +38,18 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito object InitialState : ViewState open class PollVoteState( val poll: Poll, - val showCloseButton: Boolean + val showEndPollButton: Boolean ) : ViewState open class PollVoteHiddenState( val poll: Poll, - val showCloseButton: Boolean + val showEndPollButton: Boolean ) : ViewState open class PollResultState( val poll: Poll, val showEditButton: Boolean, - val showCloseButton: Boolean + val showEndPollButton: Boolean ) : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 5fc62f79e..28af8336b 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -38,7 +38,7 @@ Date: Thu, 30 Jun 2022 14:36:02 +0200 Subject: [PATCH 37/92] improve klint score, minor refactoring Signed-off-by: Marcel Hibbe --- .../adapters/PollCreateOptionViewHolder.kt | 2 +- .../polls/adapters/PollResultHeaderItem.kt | 2 +- .../adapters/PollResultHeaderViewHolder.kt | 2 - .../talk/polls/adapters/PollResultItem.kt | 4 +- .../polls/adapters/PollResultVoterItem.kt | 2 +- .../adapters/PollResultVoterViewHolder.kt | 25 +----------- .../talk/polls/adapters/PollResultsAdapter.kt | 4 +- .../polls/repositories/PollRepositoryImpl.kt | 3 +- .../talk/polls/ui/PollCreateDialogFragment.kt | 10 ++--- .../talk/polls/ui/PollMainDialogFragment.kt | 8 ++-- .../talk/polls/ui/PollResultsFragment.kt | 40 ++++++------------- .../talk/polls/ui/PollVoteFragment.kt | 31 ++++++-------- .../polls/viewmodels/PollCreateViewModel.kt | 9 ++++- .../polls/viewmodels/PollResultsViewModel.kt | 5 ++- .../polls/viewmodels/PollVoteViewModel.kt | 10 ++--- app/src/main/res/values/strings.xml | 2 + 16 files changed, 59 insertions(+), 100 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index e8ca339ef..6d7966d90 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -12,7 +12,7 @@ class PollCreateOptionViewHolder( ) : RecyclerView.ViewHolder(binding.root) { lateinit var optionText: EmojiTextInputEditText - var textListener: TextWatcher? = null + private var textListener: TextWatcher? = null @SuppressLint("SetTextI18n") fun bind( diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt index a0dd6bfd3..3960e2824 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt @@ -14,6 +14,6 @@ data class PollResultHeaderItem( companion object { // layout is used as view type for uniqueness - public val VIEW_TYPE: Int = R.layout.poll_result_header_item + const val VIEW_TYPE: Int = R.layout.poll_result_header_item } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt index 0c73ac55e..a1ae4d9e4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt @@ -3,10 +3,8 @@ package com.nextcloud.talk.polls.adapters import android.annotation.SuppressLint import android.graphics.Typeface import com.nextcloud.talk.databinding.PollResultHeaderItemBinding -import com.nextcloud.talk.models.database.UserEntity class PollResultHeaderViewHolder( - private val user: UserEntity, override val binding: PollResultHeaderItemBinding ) : PollResultViewHolder(binding) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt index 8349c2886..932d11028 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt @@ -1,7 +1,5 @@ package com.nextcloud.talk.polls.adapters interface PollResultItem { - fun getViewType(): Int - // fun getView(inflater: LayoutInflater?, convertView: View?): View? -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt index e05943f09..d00dda861 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt @@ -13,6 +13,6 @@ data class PollResultVoterItem( companion object { // layout is used as view type for uniqueness - public val VIEW_TYPE: Int = R.layout.poll_result_voter_item + const val VIEW_TYPE: Int = R.layout.poll_result_voter_item } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt index 1e105e216..174951174 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt @@ -20,24 +20,7 @@ class PollResultVoterViewHolder( @SuppressLint("SetTextI18n") override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { val item = pollResultItem as PollResultVoterItem - - // binding.root.setOnClickListener { clickListener.onClick(pollResultVoterItem) } - - // binding.pollVoterAvatar = pollResultHeaderItem.name binding.pollVoterName.text = item.details.actorDisplayName - - // val lp = LinearLayout.LayoutParams( - // 60, - // 50 - // ) - // - // val avatar = SimpleDraweeView(binding.root.context) - // avatar.layoutParams = lp - - // val roundingParams = RoundingParams.fromCornersRadius(5f) - // roundingParams.roundAsCircle = true - // - // binding.pollVoterAvatar.hierarchy.roundingParams = roundingParams binding.pollVoterAvatar.controller = getAvatarDraweeController(item.details) } @@ -47,8 +30,7 @@ class PollResultVoterViewHolder( if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { displayName = pollDetail.actorDisplayName!! } - val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() - // .setOldController(binding.avatar.controller) + return Fresco.newDraweeControllerBuilder() .setAutoPlayAnimations(true) .setImageRequest( DisplayUtils.getImageRequestForUrl( @@ -61,10 +43,8 @@ class PollResultVoterViewHolder( ) ) .build() - return draweeController } else if (pollDetail.actorType == "users") { - val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() - // .setOldController(binding.avatar.controller) + return Fresco.newDraweeControllerBuilder() .setAutoPlayAnimations(true) .setImageRequest( DisplayUtils.getImageRequestForUrl( @@ -77,7 +57,6 @@ class PollResultVoterViewHolder( ) ) .build() - return draweeController } return null } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index 153a198e5..a9753b596 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -20,7 +20,7 @@ class PollResultsAdapter( LayoutInflater.from(parent.context), parent, false ) - return PollResultHeaderViewHolder(user, itemBinding) + return PollResultHeaderViewHolder(itemBinding) } PollResultVoterItem.VIEW_TYPE -> { val itemBinding = PollResultVoterItemBinding.inflate( @@ -32,7 +32,7 @@ class PollResultsAdapter( } val itemBinding = PollResultHeaderItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return PollResultHeaderViewHolder(user, itemBinding) + return PollResultHeaderViewHolder(itemBinding) } override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 60939718c..873e57b63 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -119,7 +119,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid private fun mapToPoll(pollResponse: PollResponse): Poll { val pollDetails = pollResponse.details?.map { it -> mapToPollDetails(it) } - val poll = Poll( + return Poll( pollResponse.id, pollResponse.question, pollResponse.options, @@ -134,7 +134,6 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid pollResponse.numVoters, pollDetails, ) - return poll } private fun convertVotes(votes: Map?): Map { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 35bd6648f..1a39a35b3 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -48,14 +48,12 @@ class PollCreateDialogFragment( override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogPollCreateBinding.inflate(LayoutInflater.from(context)) - val dialog = AlertDialog.Builder(requireContext()) + return AlertDialog.Builder(requireContext()) .setView(binding.root) .create() - - return dialog } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return binding.root } @@ -81,7 +79,7 @@ class PollCreateDialogFragment( private fun setupListeners() { binding.pollAddOptionsItem.setOnClickListener { viewModel.addOption() - adapter?.itemCount?.minus(1)?.let { it -> binding.pollCreateOptionsList.scrollToPosition(it) } + adapter?.itemCount?.minus(1)?.let { binding.pollCreateOptionsList.scrollToPosition(it) } } binding.pollDismiss.setOnClickListener { @@ -135,8 +133,8 @@ class PollCreateDialogFragment( private fun showError() { dismiss() - Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show() Log.e(TAG, "Failed to create poll") + Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show() } override fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 88859b881..252764950 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -52,7 +52,7 @@ class PollMainDialogFragment( return dialog } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return binding.root } @@ -64,7 +64,7 @@ class PollMainDialogFragment( PollMainViewModel.InitialState -> {} is PollMainViewModel.PollVoteHiddenState -> { binding.pollDetailsText.visibility = View.VISIBLE - binding.pollDetailsText.text = "You already voted for this private poll" + binding.pollDetailsText.text = context?.resources?.getString(R.string.polls_private_voted) showVoteScreen() } is PollMainViewModel.PollVoteState -> { @@ -94,9 +94,7 @@ class PollMainDialogFragment( val contentFragment = PollResultsFragment( user, - viewModel, - roomToken, - pollId + viewModel ) val transaction = childFragmentManager.beginTransaction() transaction.replace(binding.messagePollContentFragment.id, contentFragment) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index fa97a6b25..3f8a657af 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -21,7 +21,6 @@ package com.nextcloud.talk.polls.ui -import android.content.DialogInterface import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -45,9 +44,7 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollResultsFragment( private val user: UserEntity, - private val parentViewModel: PollMainViewModel, - private val roomToken: String, - private val pollId: String + private val parentViewModel: PollMainViewModel ) : Fragment(), PollResultItemClickListener { @Inject @@ -55,9 +52,7 @@ class PollResultsFragment( lateinit var viewModel: PollResultsViewModel - var _binding: DialogPollResultsBinding? = null - val binding: DialogPollResultsBinding - get() = _binding!! + lateinit var binding: DialogPollResultsBinding private var adapter: PollResultsAdapter? = null @@ -72,7 +67,7 @@ class PollResultsFragment( container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = DialogPollResultsBinding.inflate(inflater, container, false) + binding = DialogPollResultsBinding.inflate(inflater, container, false) return binding.root } @@ -98,49 +93,40 @@ class PollResultsFragment( private fun initAdapter() { adapter = PollResultsAdapter(user, this) - _binding?.pollResultsList?.adapter = adapter - _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) + binding.pollResultsList.adapter = adapter + binding.pollResultsList.layoutManager = LinearLayoutManager(context) } private fun initEditButton(showEditButton: Boolean) { if (showEditButton) { - _binding?.editVoteButton?.visibility = View.VISIBLE - _binding?.editVoteButton?.setOnClickListener { + binding.editVoteButton.visibility = View.VISIBLE + binding.editVoteButton.setOnClickListener { parentViewModel.edit() } } else { - _binding?.editVoteButton?.visibility = View.GONE + binding.editVoteButton.visibility = View.GONE } } private fun initEndPollButton(showEndPollButton: Boolean) { if (showEndPollButton) { - _binding?.pollResultsEndPollButton?.visibility = View.VISIBLE - _binding?.pollResultsEndPollButton?.setOnClickListener { + binding.pollResultsEndPollButton.visibility = View.VISIBLE + binding.pollResultsEndPollButton.setOnClickListener { AlertDialog.Builder(requireContext()) .setTitle(R.string.polls_end_poll) .setMessage(R.string.polls_end_poll_confirm) - .setPositiveButton(R.string.polls_end_poll, DialogInterface.OnClickListener { _, _ -> + .setPositiveButton(R.string.polls_end_poll) { _, _ -> parentViewModel.closePoll() - }) + } .setNegativeButton(R.string.nc_cancel, null) .show() } } else { - _binding?.pollResultsEndPollButton?.visibility = View.GONE + binding.pollResultsEndPollButton.visibility = View.GONE } } override fun onClick(pollResultHeaderItem: PollResultHeaderItem) { viewModel.filterItems() } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - companion object { - private val TAG = PollResultsFragment::class.java.simpleName - } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index eb805889e..fba95b230 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -21,7 +21,6 @@ package com.nextcloud.talk.polls.ui -import android.content.DialogInterface import android.graphics.Typeface import android.os.Bundle import android.util.Log @@ -56,9 +55,7 @@ class PollVoteFragment( lateinit var viewModel: PollVoteViewModel - var _binding: DialogPollVoteBinding? = null - val binding: DialogPollVoteBinding - get() = _binding!! + private lateinit var binding: DialogPollVoteBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -71,7 +68,7 @@ class PollVoteFragment( container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = DialogPollVoteBinding.inflate(inflater, container, false) + binding = DialogPollVoteBinding.inflate(inflater, container, false) return binding.root } @@ -93,7 +90,8 @@ class PollVoteFragment( when (state) { PollVoteViewModel.InitialState -> {} is PollVoteViewModel.PollVoteFailedState -> { - Log.d(TAG, "fail") + Log.e(TAG, "Failed to vote on poll.") + Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show() } is PollVoteViewModel.PollVoteSuccessState -> { parentViewModel.voted() @@ -101,7 +99,7 @@ class PollVoteFragment( } } - binding.pollVoteRadioGroup.setOnCheckedChangeListener { group, checkedId -> + binding.pollVoteRadioGroup.setOnCheckedChangeListener { _, checkedId -> viewModel.selectOption(checkedId, true) updateSubmitButton() } @@ -135,13 +133,13 @@ class PollVoteFragment( binding.voteOptionsCheckboxesWrapper.addView(checkBox) checkBox.isChecked = viewModel.selectedOptions.contains(index) == true - checkBox.setOnCheckedChangeListener { buttonView, isChecked -> + checkBox.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { if (poll.maxVotes == UNLIMITED_VOTES || viewModel.selectedOptions.size < poll.maxVotes) { viewModel.selectOption(index, false) } else { checkBox.isChecked = false - Toast.makeText(context, "max votes reached", Toast.LENGTH_LONG).show() + Toast.makeText(context, R.string.polls_max_votes_reached, Toast.LENGTH_LONG).show() } } else { viewModel.deSelectOption(index) @@ -170,27 +168,22 @@ class PollVoteFragment( private fun initEndPollButton(showEndPollButton: Boolean) { if (showEndPollButton) { - _binding?.pollVoteEndPollButton?.visibility = View.VISIBLE - _binding?.pollVoteEndPollButton?.setOnClickListener { + binding.pollVoteEndPollButton.visibility = View.VISIBLE + binding.pollVoteEndPollButton.setOnClickListener { AlertDialog.Builder(requireContext()) .setTitle(R.string.polls_end_poll) .setMessage(R.string.polls_end_poll_confirm) - .setPositiveButton(R.string.polls_end_poll, DialogInterface.OnClickListener { _, _ -> + .setPositiveButton(R.string.polls_end_poll) { _, _ -> parentViewModel.closePoll() - }) + } .setNegativeButton(R.string.nc_cancel, null) .show() } } else { - _binding?.pollVoteEndPollButton?.visibility = View.GONE + binding.pollVoteEndPollButton.visibility = View.GONE } } - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - companion object { private val TAG = PollVoteFragment::class.java.simpleName private const val UNLIMITED_VOTES = 0 diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index 341618d93..08f6147ce 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -1,5 +1,6 @@ package com.nextcloud.talk.polls.viewmodels +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -21,7 +22,12 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi object PollCreatedState : ViewState object PollCreationFailedState : ViewState - private val _viewState: MutableLiveData = MutableLiveData(PollCreationState(true, false)) + private val _viewState: MutableLiveData = MutableLiveData( + PollCreationState( + enableAddOptionButton = true, + enableCreatePollButton = false + ) + ) val viewState: LiveData get() = _viewState @@ -154,6 +160,7 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi } override fun onError(e: Throwable) { + Log.e(TAG, "Failed to create poll", e) _viewState.value = PollCreationFailedState } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 869fdbbf3..688b89efa 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -21,6 +21,7 @@ package com.nextcloud.talk.polls.viewmodels +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -28,11 +29,10 @@ import com.nextcloud.talk.polls.adapters.PollResultHeaderItem import com.nextcloud.talk.polls.adapters.PollResultItem import com.nextcloud.talk.polls.adapters.PollResultVoterItem import com.nextcloud.talk.polls.model.Poll -import com.nextcloud.talk.polls.repositories.PollRepository import io.reactivex.disposables.Disposable import javax.inject.Inject -class PollResultsViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { +class PollResultsViewModel @Inject constructor() : ViewModel() { sealed interface ViewState object InitialState : ViewState @@ -103,6 +103,7 @@ class PollResultsViewModel @Inject constructor(private val repository: PollRepos } else if (poll.votes != null) { return poll.numVoters } + Log.e(TAG, "something went wrong while getting amount of voters.") return 0 } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 891db9515..f87c8c3f9 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -38,7 +38,7 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito sealed interface ViewState object InitialState : ViewState open class PollVoteSuccessState(val poll: Poll) : ViewState - open class PollVoteFailedState() : ViewState + open class PollVoteFailedState : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) val viewState: LiveData @@ -60,10 +60,10 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito } fun selectOption(option: Int, isRadioBox: Boolean) { - if (isRadioBox) { - _selectedOptions = listOf(option) + _selectedOptions = if (isRadioBox) { + listOf(option) } else { - _selectedOptions = _selectedOptions.plus(option) + _selectedOptions.plus(option) } } @@ -97,7 +97,7 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito } override fun onError(e: Throwable) { - Log.d(TAG, "An error occurred: $e") + Log.e(TAG, "An error occurred: $e") _viewState.value = PollVoteFailedState() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8c950648b..fc1cfc355 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -539,8 +539,10 @@ Poll results - %1$s votes Add Option + You successfully voted for this private poll. End Poll Do you really want to end this poll? This can\'t be undone. + You can\'t vote with more options for this poll. Attachments From 1a000859bf46de47b5f44444d0bd1143a92612f2 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 30 Jun 2022 16:07:20 +0200 Subject: [PATCH 38/92] fix InstantiationException for fragments fragments must have a public, no-arg constructor. extra information must be passed as arguments. With a constructor that receives the data, the following error occurs: Fragment$InstantiationException: Unable to instantiate fragment ..... could not find Fragment constructor Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollCreateDialogFragment.kt | 19 +++++++--- .../talk/polls/ui/PollMainDialogFragment.kt | 37 +++++++++++++++---- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 1a39a35b3..2cc9f1bd7 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -25,9 +25,9 @@ import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollCreateDialogFragment( - private val roomToken: String -) : DialogFragment(), PollCreateOptionsItemListener { +class PollCreateDialogFragment() : DialogFragment(), PollCreateOptionsItemListener { + + lateinit var roomToken: String @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -42,6 +42,8 @@ class PollCreateDialogFragment( NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollCreateViewModel::class.java] + + roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! } @SuppressLint("InflateParams") @@ -150,10 +152,15 @@ class PollCreateDialogFragment( */ companion object { private val TAG = PollCreateDialogFragment::class.java.simpleName + private const val KEY_ROOM_TOKEN = "keyRoomToken" @JvmStatic - fun newInstance( - roomTokenParam: String - ): PollCreateDialogFragment = PollCreateDialogFragment(roomTokenParam) + fun newInstance(roomTokenParam: String): PollCreateDialogFragment { + val args = Bundle() + args.putString(KEY_ROOM_TOKEN, roomTokenParam) + val fragment = PollCreateDialogFragment() + fragment.arguments = args + return fragment + } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 252764950..40d71e231 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -19,12 +19,12 @@ import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollMainDialogFragment( - private val user: UserEntity, - private val pollId: String, - private val roomToken: String, - private val pollTitle: String -) : DialogFragment() { +class PollMainDialogFragment() : DialogFragment() { + + lateinit var user: UserEntity + lateinit var roomToken: String + lateinit var pollId: String + lateinit var pollTitle: String @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -37,6 +37,11 @@ class PollMainDialogFragment( NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollMainViewModel::class.java] + + user = arguments?.getParcelable(KEY_USER_ENTITY)!! + roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! + pollId = arguments?.getString(KEY_POLL_ID)!! + pollTitle = arguments?.getString(KEY_POLL_TITLE)!! } @SuppressLint("InflateParams") @@ -79,11 +84,13 @@ class PollMainDialogFragment( } private fun showVoteScreen() { - val contentFragment = PollVoteFragment( + + val contentFragment = PollVoteFragment.newInstance( viewModel, roomToken, pollId ) + val transaction = childFragmentManager.beginTransaction() transaction.replace(binding.messagePollContentFragment.id, contentFragment) transaction.commit() @@ -113,12 +120,26 @@ class PollMainDialogFragment( * Fragment creator */ companion object { + private const val KEY_USER_ENTITY = "keyUserEntity" + private const val KEY_ROOM_TOKEN = "keyRoomToken" + private const val KEY_POLL_ID = "keyPollId" + private const val KEY_POLL_TITLE = "keyPollTitle" + @JvmStatic fun newInstance( user: UserEntity, roomTokenParam: String, pollId: String, name: String - ): PollMainDialogFragment = PollMainDialogFragment(user, pollId, roomTokenParam, name) + ): PollMainDialogFragment { + val args = Bundle() + args.putParcelable(KEY_USER_ENTITY, user) + args.putString(KEY_ROOM_TOKEN, roomTokenParam) + args.putString(KEY_POLL_ID, pollId) + args.putString(KEY_POLL_TITLE, name) + val fragment = PollMainDialogFragment() + fragment.arguments = args + return fragment + } } } From 38256fc25d6dbb7fcd88aee76d38f6ea88e3cbec Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 30 Jun 2022 16:56:46 +0200 Subject: [PATCH 39/92] fix compile error from commit a12692ec (temporarily...) Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 40d71e231..0010229b8 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -84,8 +84,7 @@ class PollMainDialogFragment() : DialogFragment() { } private fun showVoteScreen() { - - val contentFragment = PollVoteFragment.newInstance( + val contentFragment = PollVoteFragment( viewModel, roomToken, pollId From 23bf0723262fd0efdb8317bb512c81429659c8fc Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 30 Jun 2022 17:35:30 +0200 Subject: [PATCH 40/92] fix InstantiationException for more fragments followup to a12692ec + get parentViewModel by ViewModelProvider Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollCreateDialogFragment.kt | 2 +- .../talk/polls/ui/PollMainDialogFragment.kt | 13 ++++---- .../talk/polls/ui/PollResultsFragment.kt | 27 ++++++++++++--- .../talk/polls/ui/PollVoteFragment.kt | 33 ++++++++++++++++--- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 2cc9f1bd7..c74d1809f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -25,7 +25,7 @@ import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollCreateDialogFragment() : DialogFragment(), PollCreateOptionsItemListener { +class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener { lateinit var roomToken: String diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 0010229b8..0467ad48d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -19,7 +19,7 @@ import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollMainDialogFragment() : DialogFragment() { +class PollMainDialogFragment : DialogFragment() { lateinit var user: UserEntity lateinit var roomToken: String @@ -63,7 +63,6 @@ class PollMainDialogFragment() : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { PollMainViewModel.InitialState -> {} @@ -84,8 +83,8 @@ class PollMainDialogFragment() : DialogFragment() { } private fun showVoteScreen() { - val contentFragment = PollVoteFragment( - viewModel, + + val contentFragment = PollVoteFragment.newInstance( roomToken, pollId ) @@ -98,10 +97,10 @@ class PollMainDialogFragment() : DialogFragment() { private fun showResultsScreen(poll: Poll) { initVotersAmount(poll.numVoters) - val contentFragment = PollResultsFragment( - user, - viewModel + val contentFragment = PollResultsFragment.newInstance( + user ) + val transaction = childFragmentManager.beginTransaction() transaction.replace(binding.messagePollContentFragment.id, contentFragment) transaction.commit() diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 3f8a657af..c2b0215a1 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -42,16 +42,16 @@ import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollResultsFragment( - private val user: UserEntity, - private val parentViewModel: PollMainViewModel -) : Fragment(), PollResultItemClickListener { +class PollResultsFragment : Fragment(), PollResultItemClickListener { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + private lateinit var parentViewModel: PollMainViewModel lateinit var viewModel: PollResultsViewModel + lateinit var user: UserEntity + lateinit var binding: DialogPollResultsBinding private var adapter: PollResultsAdapter? = null @@ -60,6 +60,10 @@ class PollResultsFragment( super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollResultsViewModel::class.java] + parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class + .java] + + user = arguments?.getParcelable(KEY_USER_ENTITY)!! } override fun onCreateView( @@ -129,4 +133,19 @@ class PollResultsFragment( override fun onClick(pollResultHeaderItem: PollResultHeaderItem) { viewModel.filterItems() } + + companion object { + private const val KEY_USER_ENTITY = "keyUserEntity" + + @JvmStatic + fun newInstance( + user: UserEntity + ): PollResultsFragment { + val args = Bundle() + args.putParcelable(KEY_USER_ENTITY, user) + val fragment = PollResultsFragment() + fragment.arguments = args + return fragment + } + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index fba95b230..862d05f60 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -44,15 +44,16 @@ import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) -class PollVoteFragment( - private val parentViewModel: PollMainViewModel, - private val roomToken: String, - private val pollId: String -) : Fragment() { +class PollVoteFragment : Fragment() { + + lateinit var roomToken: String + + lateinit var pollId: String @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + private lateinit var parentViewModel: PollMainViewModel lateinit var viewModel: PollVoteViewModel private lateinit var binding: DialogPollVoteBinding @@ -61,6 +62,12 @@ class PollVoteFragment( super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollVoteViewModel::class.java] + + parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class + .java] + + roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! + pollId = arguments?.getString(KEY_POLL_ID)!! } override fun onCreateView( @@ -187,5 +194,21 @@ class PollVoteFragment( companion object { private val TAG = PollVoteFragment::class.java.simpleName private const val UNLIMITED_VOTES = 0 + + private const val KEY_ROOM_TOKEN = "keyRoomToken" + private const val KEY_POLL_ID = "keyPollId" + + @JvmStatic + fun newInstance( + roomTokenParam: String, + pollId: String + ): PollVoteFragment { + val args = Bundle() + args.putString(KEY_ROOM_TOKEN, roomTokenParam) + args.putString(KEY_POLL_ID, pollId) + val fragment = PollVoteFragment() + fragment.arguments = args + return fragment + } } } From 4c6e4b1c4ca286ab5c97e813f07511d987622302 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 12:11:29 +0200 Subject: [PATCH 41/92] add copyright headers Signed-off-by: Marcel Hibbe --- .../polls/adapters/PollCreateOptionItem.kt | 20 +++++++++++++++++ .../adapters/PollCreateOptionViewHolder.kt | 20 +++++++++++++++++ .../adapters/PollCreateOptionsAdapter.kt | 20 +++++++++++++++++ .../adapters/PollCreateOptionsItemListener.kt | 20 +++++++++++++++++ .../polls/adapters/PollResultHeaderItem.kt | 20 +++++++++++++++++ .../adapters/PollResultHeaderViewHolder.kt | 20 +++++++++++++++++ .../talk/polls/adapters/PollResultItem.kt | 20 +++++++++++++++++ .../adapters/PollResultItemClickListener.kt | 20 +++++++++++++++++ .../polls/adapters/PollResultViewHolder.kt | 20 +++++++++++++++++ .../polls/adapters/PollResultVoterItem.kt | 20 +++++++++++++++++ .../adapters/PollResultVoterViewHolder.kt | 20 +++++++++++++++++ .../talk/polls/adapters/PollResultsAdapter.kt | 20 +++++++++++++++++ .../com/nextcloud/talk/polls/model/Poll.kt | 20 +++++++++++++++++ .../nextcloud/talk/polls/model/PollDetails.kt | 20 +++++++++++++++++ .../talk/polls/repositories/PollRepository.kt | 22 +++++++++++++++++++ .../polls/repositories/PollRepositoryImpl.kt | 3 ++- .../repositories/model/PollDetailsResponse.kt | 19 ++++++++++++++++ .../talk/polls/ui/PollCreateDialogFragment.kt | 20 +++++++++++++++++ .../talk/polls/ui/PollMainDialogFragment.kt | 20 +++++++++++++++++ .../talk/polls/ui/PollResultsFragment.kt | 3 ++- .../talk/polls/ui/PollVoteFragment.kt | 3 ++- .../polls/viewmodels/PollCreateViewModel.kt | 20 +++++++++++++++++ .../polls/viewmodels/PollMainViewModel.kt | 20 +++++++++++++++++ .../polls/viewmodels/PollResultsViewModel.kt | 19 ++++++++++++++++ .../polls/viewmodels/PollVoteViewModel.kt | 3 ++- 25 files changed, 428 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt index ae70d60d4..2f4217c8f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionItem.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters class PollCreateOptionItem( diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index 6d7966d90..2f35fddd4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import android.annotation.SuppressLint diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt index 94adc523e..30109de69 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import android.view.LayoutInflater diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt index 5cb0c53b4..30c83fc9a 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters interface PollCreateOptionsItemListener { diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt index 3960e2824..b8f14bc90 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import com.nextcloud.talk.R diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt index a1ae4d9e4..6f780e64c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import android.annotation.SuppressLint diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt index 932d11028..d4d97fd77 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters interface PollResultItem { diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt index 9da748950..f5e5b1267 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters interface PollResultItemClickListener { diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index 5eda74cce..8ec4bd447 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import androidx.recyclerview.widget.RecyclerView diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt index d00dda861..3512b4c88 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import com.nextcloud.talk.R diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt index 174951174..24d024e3f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import android.annotation.SuppressLint diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index a9753b596..8260b1641 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters import android.view.LayoutInflater diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt index f02829bfb..f96e0009e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.model data class Poll( diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt b/app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt index d4df031ae..6b025bf8f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/PollDetails.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.model data class PollDetails( diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt index 003651af0..1b097968c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -1,3 +1,25 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.repositories import com.nextcloud.talk.polls.model.Poll diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 873e57b63..20d924306 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -1,9 +1,10 @@ /* * Nextcloud Talk application * + * @author Marcel Hibbe * @author Álvaro Brey * Copyright (C) 2022 Álvaro Brey - * Copyright (C) 2022 Nextcloud GmbH + * Copyright (C) 2022 Marcel Hibbe * * 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 diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt index b5051eac4..6fb2d00d0 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt @@ -1,3 +1,22 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.repositories.model import android.os.Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index c74d1809f..2e9a11e72 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.ui import android.annotation.SuppressLint diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 0467ad48d..1ad9506d6 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.ui import android.annotation.SuppressLint diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index c2b0215a1..64ed2d784 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -1,9 +1,10 @@ /* * Nextcloud Talk application * + * @author Marcel Hibbe * @author Álvaro Brey * Copyright (C) 2022 Álvaro Brey - * Copyright (C) 2022 Nextcloud GmbH + * Copyright (C) 2022 Marcel Hibbe * * 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 diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 862d05f60..78462aa90 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -1,9 +1,10 @@ /* * Nextcloud Talk application * + * @author Marcel Hibbe * @author Álvaro Brey * Copyright (C) 2022 Álvaro Brey - * Copyright (C) 2022 Nextcloud GmbH + * Copyright (C) 2022 Marcel Hibbe * * 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 diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index 08f6147ce..71755c713 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.viewmodels import android.util.Log diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 2c3d35ee1..636ca3df9 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.viewmodels import android.util.Log diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 688b89efa..6644b487b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -18,6 +18,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.viewmodels diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index f87c8c3f9..cb8f16fc2 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -1,9 +1,10 @@ /* * Nextcloud Talk application * + * @author Marcel Hibbe * @author Álvaro Brey * Copyright (C) 2022 Álvaro Brey - * Copyright (C) 2022 Nextcloud GmbH + * Copyright (C) 2022 Marcel Hibbe * * 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 From 1b13812202031f0912f47a89c50ac6be684ed723 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 12:16:10 +0200 Subject: [PATCH 42/92] solve klint warnings Signed-off-by: Marcel Hibbe --- .../talk/polls/adapters/PollResultViewHolder.kt | 2 +- .../talk/polls/repositories/PollRepositoryImpl.kt | 2 +- .../nextcloud/talk/polls/ui/PollResultsFragment.kt | 6 ++++-- .../com/nextcloud/talk/polls/ui/PollVoteFragment.kt | 12 ++++++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index 8ec4bd447..e8da7ceee 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -27,4 +27,4 @@ abstract class PollResultViewHolder( open val binding: ViewBinding ) : RecyclerView.ViewHolder(binding.root) { abstract fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 20d924306..8de92deed 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -45,7 +45,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid options: List, resultMode: Int, maxVotes: - Int + Int ): Observable? { return ncApi.createPoll( credentials, diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 64ed2d784..451a9058b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -61,8 +61,10 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollResultsViewModel::class.java] - parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class - .java] + parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[ + PollMainViewModel::class + .java + ] user = arguments?.getParcelable(KEY_USER_ENTITY)!! } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 78462aa90..c26290720 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -64,8 +64,10 @@ class PollVoteFragment : Fragment() { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollVoteViewModel::class.java] - parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class - .java] + parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[ + PollMainViewModel::class + .java + ] roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! pollId = arguments?.getString(KEY_POLL_ID)!! @@ -164,8 +166,10 @@ class PollVoteFragment : Fragment() { } private fun areSelectedOptionsDifferentToVotedOptions(): Boolean { - return !(viewModel.votedOptions.containsAll(viewModel.selectedOptions) && - viewModel.selectedOptions.containsAll(viewModel.votedOptions)) + return !( + viewModel.votedOptions.containsAll(viewModel.selectedOptions) && + viewModel.selectedOptions.containsAll(viewModel.votedOptions) + ) } private fun makeOptionBoldIfSelfVoted(button: CompoundButton, poll: Poll, index: Int) { From 2b8ee4953b1d00b706099b498b95a386573659ca Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 12:32:20 +0200 Subject: [PATCH 43/92] solve detekt warnings Signed-off-by: Marcel Hibbe --- .../talk/polls/adapters/PollResultVoterViewHolder.kt | 7 ++++--- .../talk/polls/adapters/PollResultsAdapter.kt | 10 +++++----- .../talk/polls/viewmodels/PollResultsViewModel.kt | 12 ++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt index 24d024e3f..f88f9acf3 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt @@ -45,12 +45,13 @@ class PollResultVoterViewHolder( } private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? { + var draweeController: DraweeController? = null if (pollDetail.actorType == "guests") { var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest) if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { displayName = pollDetail.actorDisplayName!! } - return Fresco.newDraweeControllerBuilder() + draweeController = Fresco.newDraweeControllerBuilder() .setAutoPlayAnimations(true) .setImageRequest( DisplayUtils.getImageRequestForUrl( @@ -64,7 +65,7 @@ class PollResultVoterViewHolder( ) .build() } else if (pollDetail.actorType == "users") { - return Fresco.newDraweeControllerBuilder() + draweeController = Fresco.newDraweeControllerBuilder() .setAutoPlayAnimations(true) .setImageRequest( DisplayUtils.getImageRequestForUrl( @@ -78,6 +79,6 @@ class PollResultVoterViewHolder( ) .build() } - return null + return draweeController } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index 8260b1641..f70f8393c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -34,25 +34,25 @@ class PollResultsAdapter( internal var list: MutableList = ArrayList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollResultViewHolder { + var viewHolder: PollResultViewHolder? = null + when (viewType) { PollResultHeaderItem.VIEW_TYPE -> { val itemBinding = PollResultHeaderItemBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - return PollResultHeaderViewHolder(itemBinding) + viewHolder = PollResultHeaderViewHolder(itemBinding) } PollResultVoterItem.VIEW_TYPE -> { val itemBinding = PollResultVoterItemBinding.inflate( LayoutInflater.from(parent.context), parent, false ) - return PollResultVoterViewHolder(user, itemBinding) + viewHolder = PollResultVoterViewHolder(user, itemBinding) } } - - val itemBinding = PollResultHeaderItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return PollResultHeaderViewHolder(itemBinding) + return viewHolder!! } override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 6644b487b..a5581f94a 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -40,7 +40,6 @@ package com.nextcloud.talk.polls.viewmodels -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -86,7 +85,7 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { _items.value = ArrayList() val votersAmount = getVotersAmount(poll) - val oneVoteInPercent = 100 / votersAmount + val oneVoteInPercent = HUNDRED / votersAmount poll.options?.forEachIndexed { index, option -> val votersAmountForThisOption = getVotersAmountForOption(poll, index) @@ -117,13 +116,13 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { } private fun getVotersAmount(poll: Poll): Int { + var votersAmount = 0 if (poll.details != null) { - return poll.details.size + votersAmount = poll.details.size } else if (poll.votes != null) { - return poll.numVoters + votersAmount = poll.numVoters } - Log.e(TAG, "something went wrong while getting amount of voters.") - return 0 + return votersAmount } private fun getVotersAmountForOption(poll: Poll, index: Int): Int { @@ -156,5 +155,6 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { companion object { private val TAG = PollResultsViewModel::class.java.simpleName + private const val HUNDRED = 100 } } From 2988b667fe6b847b69996fb031f5bbb1568504b5 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 12:41:43 +0200 Subject: [PATCH 44/92] solve detekt warnings 2 Signed-off-by: Marcel Hibbe --- .../talk/polls/repositories/model/PollDetailsResponse.kt | 3 +-- .../nextcloud/talk/polls/repositories/model/PollResponse.kt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt index 6fb2d00d0..0d03e0172 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt @@ -38,8 +38,7 @@ data class PollDetailsResponse( @JsonField(name = ["optionId"]) var optionId: Int, - - ) : Parcelable { +) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, "", "", 0) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt index 6215fb89b..9010ee2f5 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt @@ -65,8 +65,7 @@ data class PollResponse( @JsonField(name = ["details"]) var details: ArrayList? = null, - - ) : Parcelable { +) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this("id", null, null, null, null, null, null, 0, 0, 0, null, 0, null) } From 9e8dbb70f38a8e301d18842fa12f2b2f1f527ec4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 12:56:25 +0200 Subject: [PATCH 45/92] remove livedata where it's not needed Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollCreateDialogFragment.kt | 5 +--- .../polls/viewmodels/PollCreateViewModel.kt | 28 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 2e9a11e72..6d9c38661 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -83,9 +83,6 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener super.onViewCreated(view, savedInstanceState) viewModel.options.observe(viewLifecycleOwner) { options -> adapter?.updateOptionsList(options) } - viewModel.question.observe(viewLifecycleOwner) { binding.pollCreateQuestion.setText(it) } - viewModel.privatePoll.observe(viewLifecycleOwner) { binding.pollPrivatePollCheckbox.isChecked = it } - viewModel.multipleAnswer.observe(viewLifecycleOwner) { binding.pollMultipleAnswersCheckbox.isChecked = it } binding.pollCreateOptionsList.layoutManager = LinearLayoutManager(context) @@ -118,7 +115,7 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener } override fun onTextChanged(question: CharSequence, start: Int, before: Int, count: Int) { - if (question.toString() != viewModel.question.value) { + if (question.toString() != viewModel.question) { viewModel.setQuestion(question.toString()) binding.pollCreateQuestion.setSelection(binding.pollCreateQuestion.length()) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index 71755c713..abbfdd920 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -56,16 +56,16 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi val options: LiveData> get() = _options - private var _question: MutableLiveData = MutableLiveData() - val question: LiveData + private var _question: String = "" + val question: String get() = _question - private var _privatePoll: MutableLiveData = MutableLiveData() - val privatePoll: LiveData + private var _privatePoll: Boolean = false + val privatePoll: Boolean get() = _privatePoll - private var _multipleAnswer: MutableLiveData = MutableLiveData() - val multipleAnswer: LiveData + private var _multipleAnswer: Boolean = false + val multipleAnswer: Boolean get() = _multipleAnswer private var disposable: Disposable? = null @@ -97,18 +97,18 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi fun createPoll() { var maxVotes = 1 - if (multipleAnswer.value == true) { + if (multipleAnswer) { maxVotes = 0 } var resultMode = 0 - if (privatePoll.value == true) { + if (privatePoll) { resultMode = 1 } - if (_question.value?.isNotEmpty() == true && _options.value?.isNotEmpty() == true) { + if (_question.isNotEmpty() && _options.value?.isNotEmpty() == true) { repository.createPoll( - roomToken, _question.value!!, _options.value!!.map { it.pollOption }, resultMode, + roomToken, _question, _options.value!!.map { it.pollOption }, resultMode, maxVotes ) ?.doOnSubscribe { disposable = it } @@ -119,16 +119,16 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi } fun setQuestion(question: String) { - _question.value = question + _question = question updateCreationState() } fun setPrivatePoll(checked: Boolean) { - _privatePoll.value = checked + _privatePoll = checked } fun setMultipleAnswer(checked: Boolean) { - _multipleAnswer.value = checked + _multipleAnswer = checked } fun optionsItemTextChanged() { @@ -140,7 +140,7 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi } private fun enableCreatePollButton(): Boolean { - return _question.value?.isNotEmpty() == true && atLeastTwoOptionsAreFilled() + return _question.isNotEmpty() && atLeastTwoOptionsAreFilled() } private fun atLeastTwoOptionsAreFilled(): Boolean { From cedbcaefee42a41c4a8e26762fd7f5a42561657c Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 14:07:57 +0200 Subject: [PATCH 46/92] fix percentage calculation for multiselect polls for multiselect polls the total votes must be taken instead amount of voters (one voter can have more than 1 votes..) Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/polls/model/Poll.kt | 3 ++- .../talk/polls/repositories/PollRepositoryImpl.kt | 9 +++++++++ .../talk/polls/ui/PollMainDialogFragment.kt | 6 +++--- .../talk/polls/viewmodels/PollResultsViewModel.kt | 13 +------------ 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt index f96e0009e..0405e8d2e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt @@ -33,7 +33,8 @@ data class Poll( val maxVotes: Int, val votedSelf: List?, val numVoters: Int, - val details: List? + val details: List?, + val totalVotes: Int ) { companion object { const val STATUS_OPEN: Int = 0 diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 8de92deed..7c888d0fd 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -134,6 +134,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid pollResponse.votedSelf, pollResponse.numVoters, pollDetails, + getTotalVotes(pollResponse.votes) ) } @@ -153,5 +154,13 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid pollDetailsResponse.optionId, ) } + + private fun getTotalVotes(votes: Map?): Int { + var totalVotes = 0 + votes?.forEach { + totalVotes += it.value + } + return totalVotes + } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 1ad9506d6..99a694bfb 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -115,7 +115,7 @@ class PollMainDialogFragment : DialogFragment() { } private fun showResultsScreen(poll: Poll) { - initVotersAmount(poll.numVoters) + initVotesAmount(poll.totalVotes) val contentFragment = PollResultsFragment.newInstance( user @@ -126,11 +126,11 @@ class PollMainDialogFragment : DialogFragment() { transaction.commit() } - private fun initVotersAmount(numVoters: Int) { + private fun initVotesAmount(totalVotes: Int) { binding.pollDetailsText.visibility = View.VISIBLE binding.pollDetailsText.text = String.format( resources.getString(R.string.polls_amount_voters), - numVoters + totalVotes ) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index a5581f94a..a803e628e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -84,8 +84,7 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { private fun initPollResults(poll: Poll) { _items.value = ArrayList() - val votersAmount = getVotersAmount(poll) - val oneVoteInPercent = HUNDRED / votersAmount + val oneVoteInPercent = HUNDRED / poll.totalVotes poll.options?.forEachIndexed { index, option -> val votersAmountForThisOption = getVotersAmountForOption(poll, index) @@ -115,16 +114,6 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { _items.value = tempList } - private fun getVotersAmount(poll: Poll): Int { - var votersAmount = 0 - if (poll.details != null) { - votersAmount = poll.details.size - } else if (poll.votes != null) { - votersAmount = poll.numVoters - } - return votersAmount - } - private fun getVotersAmountForOption(poll: Poll, index: Int): Int { var votersAmountForThisOption: Int? = 0 if (poll.details != null) { From 6ab5dace1c3fb7f7bb2d9c28dcf4f83e68d83e04 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 15:40:19 +0200 Subject: [PATCH 47/92] fix layouts to handle poll with many options make options scrollable replace constraint layout with linear layouts + layout_weight="1" Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_poll_main.xml | 50 ++++++------ .../main/res/layout/dialog_poll_results.xml | 50 ++++++------ app/src/main/res/layout/dialog_poll_vote.xml | 78 ++++++++++--------- .../res/layout/poll_result_header_item.xml | 2 + .../res/layout/poll_result_voter_item.xml | 2 + 5 files changed, 95 insertions(+), 87 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_main.xml b/app/src/main/res/layout/dialog_poll_main.xml index 59e5e8a84..47385ab6e 100644 --- a/app/src/main/res/layout/dialog_poll_main.xml +++ b/app/src/main/res/layout/dialog_poll_main.xml @@ -16,34 +16,37 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - - - android:contentDescription="@null" - android:src="@drawable/ic_baseline_bar_chart_24" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:tint="@color/high_emphasis_menu_icon" /> + - + + + + android:layout_weight="1" /> - + diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 28af8336b..366f21830 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -16,19 +16,19 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + tools:background="@color/white" + android:orientation="vertical"> + android:layout_height="0dp" + android:layout_weight="1"> - + android:gravity="end"> - + - + + + + diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index c6f6c5e7c..964204229 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -16,60 +16,62 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + tools:background="@color/white" + android:orientation="vertical"> - + android:layout_height="0dp" + android:layout_weight="1" + android:padding="@dimen/standard_half_padding"> + + + + + - + android:layout_height="wrap_content" + android:text="@string/polls_end_poll" + style="@style/OutlinedButton" + android:layout_marginEnd="@dimen/standard_margin" + app:cornerRadius="@dimen/button_corner_radius" /> + - - - - - - - + diff --git a/app/src/main/res/layout/poll_result_header_item.xml b/app/src/main/res/layout/poll_result_header_item.xml index edfe72461..f3d444610 100644 --- a/app/src/main/res/layout/poll_result_header_item.xml +++ b/app/src/main/res/layout/poll_result_header_item.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingStart="@dimen/standard_half_padding" + android:paddingEnd="@dimen/standard_half_padding" tools:background="@color/white"> From 385d5ccd4a5f0861c99ac4f108668ba4f060cc58 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 1 Jul 2022 15:54:06 +0200 Subject: [PATCH 48/92] fix copyrights Signed-off-by: Marcel Hibbe --- .../main/res/layout/dialog_poll_create.xml | 37 ++++++++++--------- app/src/main/res/layout/dialog_poll_main.xml | 37 ++++++++++--------- .../main/res/layout/dialog_poll_results.xml | 37 ++++++++++--------- app/src/main/res/layout/dialog_poll_vote.xml | 37 ++++++++++--------- .../res/layout/poll_create_options_item.xml | 20 ++++++++++ .../res/layout/poll_result_header_item.xml | 20 ++++++++++ .../res/layout/poll_result_voter_item.xml | 20 ++++++++++ 7 files changed, 136 insertions(+), 72 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_create.xml b/app/src/main/res/layout/dialog_poll_create.xml index 23989ba33..46c6cb9b9 100644 --- a/app/src/main/res/layout/dialog_poll_create.xml +++ b/app/src/main/res/layout/dialog_poll_create.xml @@ -1,21 +1,22 @@ - + - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2, - as published by the Free Software Foundation. - - 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 . ---> + - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2, - as published by the Free Software Foundation. - - 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 . ---> + - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2, - as published by the Free Software Foundation. - - 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 . ---> + + + + Date: Fri, 1 Jul 2022 18:29:43 +0200 Subject: [PATCH 49/92] fix text of submit button + rename variables Signed-off-by: Marcel Hibbe --- .../talk/polls/viewmodels/PollMainViewModel.kt | 14 +++++++------- app/src/main/res/layout/dialog_poll_vote.xml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 636ca3df9..8f5a43347 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -126,21 +126,21 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito } override fun onError(e: Throwable) { - Log.d(TAG, "An error occurred: $e") + Log.e(TAG, "An error occurred: $e") } override fun onComplete() { - val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) + val showEndPollButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) if (votedForOpenHiddenPoll(poll)) { - _viewState.value = PollVoteHiddenState(poll, showCloseButton) + _viewState.value = PollVoteHiddenState(poll, showEndPollButton) } else if (editPoll && poll.status == Poll.STATUS_OPEN) { - _viewState.value = PollVoteState(poll, showCloseButton) + _viewState.value = PollVoteState(poll, showEndPollButton) editPoll = false } else if (poll.status == Poll.STATUS_CLOSED || poll.votedSelf?.isNotEmpty() == true) { setPollResultState(poll) } else if (poll.votedSelf.isNullOrEmpty()) { - _viewState.value = PollVoteState(poll, showCloseButton) + _viewState.value = PollVoteState(poll, showEndPollButton) } else { Log.w(TAG, "unknown poll state") } @@ -149,9 +149,9 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito private fun setPollResultState(poll: Poll) { val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC - val showCloseButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) + val showEndPollButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) - _viewState.value = PollResultState(poll, showEditButton, showCloseButton) + _viewState.value = PollResultState(poll, showEditButton, showEndPollButton) } private fun votedForOpenHiddenPoll(poll: Poll): Boolean { diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index f41fef649..1ab4c2c9a 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -70,7 +70,7 @@ android:id="@+id/poll_vote_submit_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/edit" + android:text="@string/nc_common_submit" android:theme="@style/Button.Primary" app:cornerRadius="@dimen/button_corner_radius" /> From 40f20c56d6b9e15009670441700ae7dfbeaf3c18 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 14:45:12 +0200 Subject: [PATCH 50/92] update poll UI for moderators etc - allow to end poll for moderators - allow to see voters amount for voting screen for moderators and creators of the poll - replace UserEntity with User - show numVoters instead of votes - minor refactoring Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 4 +- .../talk/adapters/messages/MessagePayload.kt | 3 +- .../OutcomingPollMessageViewHolder.kt | 4 +- .../talk/controllers/ChatController.kt | 2 +- .../adapters/PollResultVoterViewHolder.kt | 8 +-- .../talk/polls/adapters/PollResultsAdapter.kt | 4 +- .../talk/polls/ui/PollMainDialogFragment.kt | 58 +++++++++++++------ .../talk/polls/ui/PollResultsFragment.kt | 8 +-- .../polls/viewmodels/PollMainViewModel.kt | 52 ++++++++++++----- app/src/main/res/layout/dialog_poll_main.xml | 42 +++++++++++++- app/src/main/res/values/strings.xml | 3 +- 11 files changed, 137 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 51bab9f80..b6c1744db 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -121,12 +121,14 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH if (pollId != null && pollName != null) { binding.messagePollTitle.text = pollName - val roomToken = (payload as? MessagePayload)!!.roomToken + val roomToken = (payload as? MessagePayload)!!.currentConversation.token!! + val isOwnerOrModerator = (payload as? MessagePayload)!!.currentConversation.isParticipantOwnerOrModerator binding.bubble.setOnClickListener { val pollVoteDialog = PollMainDialogFragment.newInstance( message.activeUser!!, roomToken, + isOwnerOrModerator, pollId, pollName ) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt index 59be4b481..9444eb63b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt @@ -1,8 +1,9 @@ package com.nextcloud.talk.adapters.messages +import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet data class MessagePayload( - val roomToken: String, + var currentConversation: Conversation, val profileBottomSheet: ProfileBottomSheet ) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index 15b3d8ab0..6583a2719 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -137,12 +137,14 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag if (pollId != null && pollName != null) { binding.messagePollTitle.text = pollName - val roomToken = (payload as? MessagePayload)!!.roomToken + val roomToken = (payload as? MessagePayload)!!.currentConversation.token!! + val isOwnerOrModerator = (payload as? MessagePayload)!!.currentConversation.isParticipantOwnerOrModerator binding.bubble.setOnClickListener { val pollVoteDialog = PollMainDialogFragment.newInstance( message.activeUser!!, roomToken, + isOwnerOrModerator, pollId, pollName ) 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 0927abf94..d271fc8dc 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -487,7 +487,7 @@ class ChatController(args: Bundle) : val messageHolders = MessageHolders() val profileBottomSheet = ProfileBottomSheet(ncApi!!, conversationUser!!, router) - val payload = MessagePayload(roomToken!!, profileBottomSheet) + val payload = MessagePayload(currentConversation!!, profileBottomSheet) messageHolders.setIncomingTextConfig( MagicIncomingTextMessageViewHolder::class.java, diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt index f88f9acf3..756887c4b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt @@ -26,14 +26,14 @@ 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.PollResultVoterItemBinding -import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.polls.model.PollDetails import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils class PollResultVoterViewHolder( - private val user: UserEntity, + private val user: User, override val binding: PollResultVoterItemBinding ) : PollResultViewHolder(binding) { @@ -60,7 +60,7 @@ class PollResultVoterViewHolder( displayName, false ), - null + user ) ) .build() @@ -74,7 +74,7 @@ class PollResultVoterViewHolder( pollDetail.actorId, false ), - null + user ) ) .build() diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index f70f8393c..9fa3b1dc2 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -23,12 +23,12 @@ package com.nextcloud.talk.polls.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.PollResultHeaderItemBinding import com.nextcloud.talk.databinding.PollResultVoterItemBinding -import com.nextcloud.talk.models.database.UserEntity class PollResultsAdapter( - private val user: UserEntity, + private val user: User, private val clickListener: PollResultItemClickListener, ) : RecyclerView.Adapter() { internal var list: MutableList = ArrayList() diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 99a694bfb..6ca06436f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -32,17 +32,17 @@ import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.DialogPollMainBinding -import com.nextcloud.talk.models.database.UserEntity -import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollMainDialogFragment : DialogFragment() { - lateinit var user: UserEntity + lateinit var user: User lateinit var roomToken: String + private var isOwnerOrModerator: Boolean = false lateinit var pollId: String lateinit var pollTitle: String @@ -60,6 +60,7 @@ class PollMainDialogFragment : DialogFragment() { user = arguments?.getParcelable(KEY_USER_ENTITY)!! roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! + isOwnerOrModerator = arguments?.getBoolean(KEY_OWNER_OR_MODERATOR)!! pollId = arguments?.getString(KEY_POLL_ID)!! pollTitle = arguments?.getString(KEY_POLL_TITLE)!! } @@ -83,19 +84,28 @@ class PollMainDialogFragment : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + viewModel.setIsOwnerOrModerator(isOwnerOrModerator) + viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { PollMainViewModel.InitialState -> {} is PollMainViewModel.PollVoteHiddenState -> { - binding.pollDetailsText.visibility = View.VISIBLE - binding.pollDetailsText.text = context?.resources?.getString(R.string.polls_private_voted) + binding.pollVotedHidden.visibility = View.VISIBLE + initVotersAmount(state.showVotersAmount, state.poll.numVoters, false) showVoteScreen() } is PollMainViewModel.PollVoteState -> { - binding.pollDetailsText.visibility = View.GONE + binding.pollVotedHidden.visibility = View.GONE + initVotersAmount(state.showVotersAmount, state.poll.numVoters, false) showVoteScreen() } - is PollMainViewModel.PollResultState -> showResultsScreen(state.poll) + is PollMainViewModel.PollResultState -> { + binding.pollVotedHidden.visibility = View.GONE + initVotersAmount(state.showVotersAmount, state.poll.numVoters, true) + showResultsScreen() + } + else -> {} } } @@ -103,7 +113,6 @@ class PollMainDialogFragment : DialogFragment() { } private fun showVoteScreen() { - val contentFragment = PollVoteFragment.newInstance( roomToken, pollId @@ -114,9 +123,7 @@ class PollMainDialogFragment : DialogFragment() { transaction.commit() } - private fun showResultsScreen(poll: Poll) { - initVotesAmount(poll.totalVotes) - + private fun showResultsScreen() { val contentFragment = PollResultsFragment.newInstance( user ) @@ -126,12 +133,24 @@ class PollMainDialogFragment : DialogFragment() { transaction.commit() } - private fun initVotesAmount(totalVotes: Int) { - binding.pollDetailsText.visibility = View.VISIBLE - binding.pollDetailsText.text = String.format( - resources.getString(R.string.polls_amount_voters), - totalVotes - ) + private fun initVotersAmount(showVotersAmount: Boolean, numVoters: Int, showResultSubtitle: Boolean) { + if (showVotersAmount) { + binding.pollVotesAmount.visibility = View.VISIBLE + binding.pollVotesAmount.text = String.format( + resources.getString(R.string.polls_amount_voters), + numVoters + ) + } else { + binding.pollVotesAmount.visibility = View.GONE + } + + if (showResultSubtitle) { + binding.pollResultsSubtitle.visibility = View.VISIBLE + binding.pollResultsSubtitleSeperator.visibility = View.VISIBLE + } else { + binding.pollResultsSubtitle.visibility = View.GONE + binding.pollResultsSubtitleSeperator.visibility = View.GONE + } } /** @@ -140,19 +159,22 @@ class PollMainDialogFragment : DialogFragment() { companion object { private const val KEY_USER_ENTITY = "keyUserEntity" private const val KEY_ROOM_TOKEN = "keyRoomToken" + private const val KEY_OWNER_OR_MODERATOR = "keyIsOwnerOrModerator" private const val KEY_POLL_ID = "keyPollId" private const val KEY_POLL_TITLE = "keyPollTitle" @JvmStatic fun newInstance( - user: UserEntity, + user: User, roomTokenParam: String, + isOwnerOrModerator: Boolean, pollId: String, name: String ): PollMainDialogFragment { val args = Bundle() args.putParcelable(KEY_USER_ENTITY, user) args.putString(KEY_ROOM_TOKEN, roomTokenParam) + args.putBoolean(KEY_OWNER_OR_MODERATOR, isOwnerOrModerator) args.putString(KEY_POLL_ID, pollId) args.putString(KEY_POLL_TITLE, name) val fragment = PollMainDialogFragment() diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 451a9058b..39bcfb918 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -33,8 +33,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.DialogPollResultsBinding -import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.polls.adapters.PollResultHeaderItem import com.nextcloud.talk.polls.adapters.PollResultItemClickListener import com.nextcloud.talk.polls.adapters.PollResultsAdapter @@ -51,7 +51,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { private lateinit var parentViewModel: PollMainViewModel lateinit var viewModel: PollResultsViewModel - lateinit var user: UserEntity + lateinit var user: User lateinit var binding: DialogPollResultsBinding @@ -108,7 +108,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { if (showEditButton) { binding.editVoteButton.visibility = View.VISIBLE binding.editVoteButton.setOnClickListener { - parentViewModel.edit() + parentViewModel.editVotes() } } else { binding.editVoteButton.visibility = View.GONE @@ -142,7 +142,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { @JvmStatic fun newInstance( - user: UserEntity + user: User ): PollResultsFragment { val args = Bundle() args.putParcelable(KEY_USER_ENTITY, user) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 8f5a43347..44c2c8ffe 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -52,24 +52,29 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito private lateinit var roomToken: String private lateinit var pollId: String - private var editPoll: Boolean = false + private var isOwnerOrModerator: Boolean = false + + private var editVotes: Boolean = false sealed interface ViewState object InitialState : ViewState open class PollVoteState( val poll: Poll, + val showVotersAmount: Boolean, val showEndPollButton: Boolean ) : ViewState open class PollVoteHiddenState( val poll: Poll, + val showVotersAmount: Boolean, val showEndPollButton: Boolean ) : ViewState open class PollResultState( val poll: Poll, - val showEditButton: Boolean, - val showEndPollButton: Boolean + val showVotersAmount: Boolean, + val showEndPollButton: Boolean, + val showEditButton: Boolean ) : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) @@ -89,8 +94,8 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito loadPoll() } - fun edit() { - editPoll = true + fun editVotes() { + editVotes = true loadPoll() } @@ -130,28 +135,34 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito } override fun onComplete() { - val showEndPollButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) + val showEndPollButton = showEndPollButton(poll) + val showVotersAmount = showVotersAmount(poll) if (votedForOpenHiddenPoll(poll)) { - _viewState.value = PollVoteHiddenState(poll, showEndPollButton) - } else if (editPoll && poll.status == Poll.STATUS_OPEN) { - _viewState.value = PollVoteState(poll, showEndPollButton) - editPoll = false + _viewState.value = PollVoteHiddenState(poll, showVotersAmount, showEndPollButton) + } else if (editVotes && poll.status == Poll.STATUS_OPEN) { + _viewState.value = PollVoteState(poll, false, showEndPollButton) + editVotes = false } else if (poll.status == Poll.STATUS_CLOSED || poll.votedSelf?.isNotEmpty() == true) { - setPollResultState(poll) + val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC + _viewState.value = PollResultState(poll, showVotersAmount, showEndPollButton, showEditButton) } else if (poll.votedSelf.isNullOrEmpty()) { - _viewState.value = PollVoteState(poll, showEndPollButton) + _viewState.value = PollVoteState(poll, showVotersAmount, showEndPollButton) } else { Log.w(TAG, "unknown poll state") } } } - private fun setPollResultState(poll: Poll) { - val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC - val showEndPollButton = poll.status == Poll.STATUS_OPEN && isPollCreatedByCurrentUser(poll) + private fun showEndPollButton(poll: Poll): Boolean { + return poll.status == Poll.STATUS_OPEN && (isPollCreatedByCurrentUser(poll) || isOwnerOrModerator) + } - _viewState.value = PollResultState(poll, showEditButton, showEndPollButton) + private fun showVotersAmount(poll: Poll): Boolean { + return votedForPublicPoll(poll) || + poll.status == Poll.STATUS_CLOSED || + isOwnerOrModerator || + isPollCreatedByCurrentUser(poll) } private fun votedForOpenHiddenPoll(poll: Poll): Boolean { @@ -160,10 +171,19 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito poll.votedSelf?.isNotEmpty() == true } + private fun votedForPublicPoll(poll: Poll): Boolean { + return poll.resultMode == Poll.RESULT_MODE_PUBLIC && + poll.votedSelf?.isNotEmpty() == true + } + private fun isPollCreatedByCurrentUser(poll: Poll): Boolean { return userUtils.currentUser?.userId == poll.actorId } + fun setIsOwnerOrModerator(ownerOrModerator: Boolean) { + isOwnerOrModerator = ownerOrModerator + } + companion object { private val TAG = PollMainViewModel::class.java.simpleName } diff --git a/app/src/main/res/layout/dialog_poll_main.xml b/app/src/main/res/layout/dialog_poll_main.xml index cbb01106c..70f40d028 100644 --- a/app/src/main/res/layout/dialog_poll_main.xml +++ b/app/src/main/res/layout/dialog_poll_main.xml @@ -49,13 +49,51 @@ + + + + + + + + + + + + + android:text="@string/polls_private_voted" /> No search results - Poll results - %1$s votes + %1$s voters Add Option You successfully voted for this private poll. End Poll Do you really want to end this poll? This can\'t be undone. You can\'t vote with more options for this poll. + Results Attachments From a8afb6174502835101a6e0985aa1947f2d42a1e2 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 15:19:37 +0200 Subject: [PATCH 51/92] always focus new empty poll option in poll create dialog Signed-off-by: Marcel Hibbe --- .../talk/polls/adapters/PollCreateOptionViewHolder.kt | 7 ++++++- .../talk/polls/adapters/PollCreateOptionsAdapter.kt | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index 2f35fddd4..edb223103 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -38,7 +38,8 @@ class PollCreateOptionViewHolder( fun bind( pollCreateOptionItem: PollCreateOptionItem, itemsListener: PollCreateOptionsItemListener, - position: Int + position: Int, + focus: Boolean ) { textListener?.let { @@ -47,6 +48,10 @@ class PollCreateOptionViewHolder( binding.pollOptionText.setText(pollCreateOptionItem.pollOption) + if (focus) { + binding.pollOptionText.requestFocus() + } + binding.pollOptionDelete.setOnClickListener { itemsListener.onRemoveOptionsItemClick(pollCreateOptionItem, position) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt index 30109de69..39c8f7d7f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsAdapter.kt @@ -39,7 +39,13 @@ class PollCreateOptionsAdapter( override fun onBindViewHolder(holder: PollCreateOptionViewHolder, position: Int) { val currentItem = list[position] - holder.bind(currentItem, clickListener, position) + var focus = false + + if (list.size - 1 == position && currentItem.pollOption.isBlank()) { + focus = true + } + + holder.bind(currentItem, clickListener, position, focus) } override fun getItemCount(): Int { From 3b4be83ea7617cdc7862dff8fbd70d421f83e1a3 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 15:32:25 +0200 Subject: [PATCH 52/92] calculate percent with voters instead votes Signed-off-by: Marcel Hibbe --- .../main/java/com/nextcloud/talk/polls/model/Poll.kt | 3 +-- .../talk/polls/repositories/PollRepositoryImpl.kt | 11 +---------- .../talk/polls/viewmodels/PollResultsViewModel.kt | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt index 0405e8d2e..f96e0009e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/model/Poll.kt @@ -33,8 +33,7 @@ data class Poll( val maxVotes: Int, val votedSelf: List?, val numVoters: Int, - val details: List?, - val totalVotes: Int + val details: List? ) { companion object { const val STATUS_OPEN: Int = 0 diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 7c888d0fd..76d627c31 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -133,8 +133,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid pollResponse.maxVotes, pollResponse.votedSelf, pollResponse.numVoters, - pollDetails, - getTotalVotes(pollResponse.votes) + pollDetails ) } @@ -154,13 +153,5 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid pollDetailsResponse.optionId, ) } - - private fun getTotalVotes(votes: Map?): Int { - var totalVotes = 0 - votes?.forEach { - totalVotes += it.value - } - return totalVotes - } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index a803e628e..36c0353c8 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -84,7 +84,7 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { private fun initPollResults(poll: Poll) { _items.value = ArrayList() - val oneVoteInPercent = HUNDRED / poll.totalVotes + val oneVoteInPercent = HUNDRED / poll.numVoters poll.options?.forEachIndexed { index, option -> val votersAmountForThisOption = getVotersAmountForOption(poll, index) From f10b76d430d926f51a5afc6c1fae20ca6e738403 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 15:50:35 +0200 Subject: [PATCH 53/92] check talk-polls capability Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/ui/dialog/AttachmentDialog.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt index 2da8b2d76..13a0564fb 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt @@ -43,6 +43,12 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle setContentView(dialogAttachmentBinding.root) window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + initItemsStrings() + initItemsVisibility() + initItemsClickListeners() + } + + private fun initItemsStrings() { var serverName = CapabilitiesUtilNew.getServerName(chatController.conversationUser) dialogAttachmentBinding.txtAttachFileFromCloud.text = chatController.resources?.let { if (serverName.isNullOrEmpty()) { @@ -50,7 +56,9 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle } String.format(it.getString(R.string.nc_upload_from_cloud), serverName) } + } + private fun initItemsVisibility() { if (!CapabilitiesUtilNew.hasSpreedFeatureCapability( chatController.conversationUser, "geo-location-sharing" @@ -59,6 +67,12 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle dialogAttachmentBinding.menuShareLocation.visibility = View.GONE } + if (!CapabilitiesUtilNew.hasSpreedFeatureCapability(chatController.conversationUser, "talk-polls")) { + dialogAttachmentBinding.menuAttachPoll.visibility = View.GONE + } + } + + private fun initItemsClickListeners() { dialogAttachmentBinding.menuShareLocation.setOnClickListener { chatController.showShareLocationScreen() dismiss() From 9afe4e44d60dc40a29557a27a5605c49c795ccf5 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 16:12:46 +0200 Subject: [PATCH 54/92] fix LiveData value assignment nullability mismatch Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt | 4 +++- .../nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 39bcfb918..468f26d86 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -92,7 +92,9 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { viewModel.items.observe(viewLifecycleOwner) { val adapter = PollResultsAdapter(user, this).apply { - list = it + if (it != null) { + list = it + } } binding.pollResultsList.adapter = adapter } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 36c0353c8..5633394f2 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -65,8 +65,8 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { private var _unfilteredItems: ArrayList = ArrayList() - private var _items: MutableLiveData> = MutableLiveData>() - val items: LiveData> + private var _items: MutableLiveData?> = MutableLiveData?>() + val items: MutableLiveData?> get() = _items private var disposable: Disposable? = null From e77fb7cefb3b6a637592614dd2bbd41d6868dfce Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 16:50:53 +0200 Subject: [PATCH 55/92] fix lint warnings Signed-off-by: Marcel Hibbe --- .../main/res/drawable/ic_comment_white.xml | 29 ------------------- .../main/res/layout/dialog_poll_create.xml | 10 +++---- .../res/layout/poll_create_options_item.xml | 4 ++- app/src/main/res/values/strings.xml | 5 ++++ 4 files changed, 13 insertions(+), 35 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_comment_white.xml diff --git a/app/src/main/res/drawable/ic_comment_white.xml b/app/src/main/res/drawable/ic_comment_white.xml deleted file mode 100644 index 74f759c9e..000000000 --- a/app/src/main/res/drawable/ic_comment_white.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - diff --git a/app/src/main/res/layout/dialog_poll_create.xml b/app/src/main/res/layout/dialog_poll_create.xml index 46c6cb9b9..78f35ba50 100644 --- a/app/src/main/res/layout/dialog_poll_create.xml +++ b/app/src/main/res/layout/dialog_poll_create.xml @@ -37,7 +37,7 @@ android:layout_height="wrap_content" android:textColor="@color/colorPrimary" android:textStyle="bold" - android:text="Question" /> + android:text="@string/polls_question" /> + android:text="@string/polls_options" /> + android:text="@string/polls_settings" /> + android:text="@string/polls_private_poll" /> + android:text="@string/polls_multiple_answers" /> + android:singleLine="true" + android:inputType="text" + tools:ignore="Autofill,LabelFor" /> Do you really want to end this poll? This can\'t be undone. You can\'t vote with more options for this poll. Results + Question + Options + Settings + Private poll + Multiple answers Attachments From 2ac1567f5dd4e1ee8896e5da4e2336784ce1c9c6 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 17:01:55 +0200 Subject: [PATCH 56/92] delete empty options on poll creation Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index abbfdd920..fcb38635c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -106,6 +106,8 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi resultMode = 1 } + _options.value = _options.value?.filter { it.pollOption.isNotEmpty() } as ArrayList + if (_question.isNotEmpty() && _options.value?.isNotEmpty() == true) { repository.createPoll( roomToken, _question, _options.value!!.map { it.pollOption }, resultMode, From 6b8c83f55363befb96a17708b243f4c63a50eab1 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 14 Jul 2022 17:21:26 +0200 Subject: [PATCH 57/92] add dismiss button for edit votes mode Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/models/json/chat/ChatMessage.kt | 2 +- .../com/nextcloud/talk/polls/ui/PollVoteFragment.kt | 13 +++++++++++++ .../talk/polls/viewmodels/PollMainViewModel.kt | 11 ++++++++--- app/src/main/res/layout/dialog_poll_vote.xml | 11 +++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index c47293fd6..1d68ce625 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt @@ -125,7 +125,7 @@ data class ChatMessage( var voiceMessageDownloadProgress: Int = 0, ) : Parcelable, MessageContentType, MessageContentType.Image { - // TODO: messageTypesToIgnore is weird. must be deleted by refactoring! + // messageTypesToIgnore is weird. must be deleted by refactoring!!! @JsonIgnore var messageTypesToIgnore = Arrays.asList( MessageType.REGULAR_TEXT_MESSAGE, diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index c26290720..c8adb9448 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -89,6 +89,7 @@ class PollVoteFragment : Fragment() { initPollOptions(state.poll) initEndPollButton(state.showEndPollButton) updateSubmitButton() + updateDismissEditButton(state.showDismissEditButton) } else if (state is PollMainViewModel.PollVoteHiddenState) { initPollOptions(state.poll) initEndPollButton(state.showEndPollButton) @@ -117,6 +118,18 @@ class PollVoteFragment : Fragment() { binding.pollVoteSubmitButton.setOnClickListener { viewModel.vote(roomToken, pollId) } + + binding.pollVoteEditDismiss.setOnClickListener { + parentViewModel.dismissEditVotes() + } + } + + private fun updateDismissEditButton(showDismissEditButton: Boolean) { + if (showDismissEditButton) { + binding.pollVoteEditDismiss.visibility = View.VISIBLE + } else { + binding.pollVoteEditDismiss.visibility = View.GONE + } } private fun initPollOptions(poll: Poll) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 44c2c8ffe..2cca1ac87 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -61,7 +61,8 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito open class PollVoteState( val poll: Poll, val showVotersAmount: Boolean, - val showEndPollButton: Boolean + val showEndPollButton: Boolean, + val showDismissEditButton: Boolean ) : ViewState open class PollVoteHiddenState( @@ -99,6 +100,10 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito loadPoll() } + fun dismissEditVotes() { + loadPoll() + } + private fun loadPoll() { repository.getPoll(roomToken, pollId) ?.doOnSubscribe { disposable = it } @@ -141,13 +146,13 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito if (votedForOpenHiddenPoll(poll)) { _viewState.value = PollVoteHiddenState(poll, showVotersAmount, showEndPollButton) } else if (editVotes && poll.status == Poll.STATUS_OPEN) { - _viewState.value = PollVoteState(poll, false, showEndPollButton) + _viewState.value = PollVoteState(poll, false, showEndPollButton, true) editVotes = false } else if (poll.status == Poll.STATUS_CLOSED || poll.votedSelf?.isNotEmpty() == true) { val showEditButton = poll.status == Poll.STATUS_OPEN && poll.resultMode == Poll.RESULT_MODE_PUBLIC _viewState.value = PollResultState(poll, showVotersAmount, showEndPollButton, showEditButton) } else if (poll.votedSelf.isNullOrEmpty()) { - _viewState.value = PollVoteState(poll, showVotersAmount, showEndPollButton) + _viewState.value = PollVoteState(poll, showVotersAmount, showEndPollButton, false) } else { Log.w(TAG, "unknown poll state") } diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 1ab4c2c9a..b3cc0ddb4 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -66,6 +66,17 @@ android:layout_marginEnd="@dimen/standard_margin" app:cornerRadius="@dimen/button_corner_radius" /> + + Date: Fri, 15 Jul 2022 09:19:07 +0200 Subject: [PATCH 58/92] fix cursor jumping to end when editing question setting the cursor to the end was just a (buggy) workaround when the question was of type liveData. As it's not liveData anymore, this workaround was now useless Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 6d9c38661..d0ccefeb5 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -117,7 +117,6 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener override fun onTextChanged(question: CharSequence, start: Int, before: Int, count: Int) { if (question.toString() != viewModel.question) { viewModel.setQuestion(question.toString()) - binding.pollCreateQuestion.setSelection(binding.pollCreateQuestion.length()) } } }) From 91fc3c311dc8068b94d57ef60ea583a9181dca5f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 15 Jul 2022 10:35:31 +0200 Subject: [PATCH 59/92] allow question to be multiline Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_poll_create.xml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_create.xml b/app/src/main/res/layout/dialog_poll_create.xml index 78f35ba50..d1d1b73a1 100644 --- a/app/src/main/res/layout/dialog_poll_create.xml +++ b/app/src/main/res/layout/dialog_poll_create.xml @@ -26,12 +26,6 @@ android:padding="@dimen/standard_padding" tools:background="@color/white"> - - - - - - + android:layout_height="wrap_content" /> Date: Tue, 19 Jul 2022 10:46:06 +0200 Subject: [PATCH 60/92] add copyrights Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 2 +- .../talk/adapters/messages/MessagePayload.kt | 20 +++++++++++++++++++ .../OutcomingPollMessageViewHolder.kt | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index b6c1744db..4aab36277 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -2,7 +2,7 @@ * Nextcloud Talk application * * @author Marcel Hibbe - * Copyright (C) 2021 Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe * * 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 diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt index 9444eb63b..df6d22a8f 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MessagePayload.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.adapters.messages import com.nextcloud.talk.models.json.conversations.Conversation diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index 6583a2719..bcd84bb48 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -3,7 +3,7 @@ * * @author Marcel Hibbe * Copyright (C) 2017-2018 Mario Danic - * Copyright (C) 2021 Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe * * 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 From 4388d66b23f02b318100f651debb65257f8a9d17 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 11:10:02 +0200 Subject: [PATCH 61/92] use CurrentUserProviderNew and User for polls use CurrentUserProviderNew and User instead of CurrentUserProvider and UserEntity Signed-off-by: Marcel Hibbe --- .../talk/dagger/modules/RepositoryModule.kt | 7 ++-- .../polls/repositories/PollRepositoryImpl.kt | 37 +++++-------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt index 55d9580aa..47e3747b3 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt @@ -24,13 +24,13 @@ package com.nextcloud.talk.dagger.modules import com.nextcloud.talk.api.NcApi -import com.nextcloud.talk.polls.repositories.PollRepository -import com.nextcloud.talk.polls.repositories.PollRepositoryImpl import com.nextcloud.talk.data.source.local.TalkDatabase import com.nextcloud.talk.data.storage.ArbitraryStoragesRepository import com.nextcloud.talk.data.storage.ArbitraryStoragesRepositoryImpl import com.nextcloud.talk.data.user.UsersRepository import com.nextcloud.talk.data.user.UsersRepositoryImpl +import com.nextcloud.talk.polls.repositories.PollRepository +import com.nextcloud.talk.polls.repositories.PollRepositoryImpl import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepository import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepositoryImpl import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository @@ -38,6 +38,7 @@ import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepositoryImpl import com.nextcloud.talk.shareditems.repositories.SharedItemsRepository import com.nextcloud.talk.shareditems.repositories.SharedItemsRepositoryImpl import com.nextcloud.talk.utils.database.user.CurrentUserProvider +import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import dagger.Module import dagger.Provides import okhttp3.OkHttpClient @@ -55,7 +56,7 @@ class RepositoryModule { } @Provides - fun provideDialogPollRepository(ncApi: NcApi, userProvider: CurrentUserProvider): PollRepository { + fun provideDialogPollRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): PollRepository { return PollRepositoryImpl(ncApi, userProvider) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index 76d627c31..fa04db86d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -23,21 +23,20 @@ package com.nextcloud.talk.polls.repositories import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.model.PollDetails import com.nextcloud.talk.polls.repositories.model.PollDetailsResponse import com.nextcloud.talk.polls.repositories.model.PollResponse import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.database.user.CurrentUserProvider +import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import io.reactivex.Observable -class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvider: CurrentUserProvider) : +class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvider: CurrentUserProviderNew) : PollRepository { - val credentials = ApiUtils.getCredentials( - currentUserProvider.currentUser?.username, - currentUserProvider.currentUser?.token - ) + val currentUser: User = currentUserProvider.currentUser.blockingGet() + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) override fun createPoll( roomToken: String, @@ -50,7 +49,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.createPoll( credentials, ApiUtils.getUrlForPoll( - currentUserProvider.currentUser?.baseUrl, + currentUser.baseUrl, roomToken ), question, @@ -65,29 +64,11 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.getPoll( credentials, ApiUtils.getUrlForPoll( - currentUserProvider.currentUser?.baseUrl, + currentUser.baseUrl, roomToken, pollId ), ).map { mapToPoll(it.ocs?.data!!) } - - // return Observable.just( - // Poll( - // id = "aaa", - // question = "what if?", - // options = listOf("yes", "no", "maybe", "I don't know"), - // votes = listOf(0, 0, 0, 0), - // actorType = "", - // actorId = "", - // actorDisplayName = "", - // status = 0, - // resultMode = 0, - // maxVotes = 1, - // votedSelf = listOf(0, 0, 0, 0), - // numVoters = 0, - // details = emptyList() - // ) - // ) } override fun vote(roomToken: String, pollId: String, options: List): Observable? { @@ -95,7 +76,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.votePoll( credentials, ApiUtils.getUrlForPoll( - currentUserProvider.currentUser?.baseUrl, + currentUser.baseUrl, roomToken, pollId ), @@ -108,7 +89,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.closePoll( credentials, ApiUtils.getUrlForPoll( - currentUserProvider.currentUser?.baseUrl, + currentUser.baseUrl, roomToken, pollId ), From 4b6a314514f5ae8d86f52553d2dd076355708c9f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 11:29:06 +0200 Subject: [PATCH 62/92] remove unused viewState Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 5633394f2..205d2a9af 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -40,7 +40,6 @@ package com.nextcloud.talk.polls.viewmodels -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.nextcloud.talk.polls.adapters.PollResultHeaderItem @@ -59,10 +58,6 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { val poll: Poll? get() = _poll - private val _viewState: MutableLiveData = MutableLiveData(InitialState) - val viewState: LiveData - get() = _viewState - private var _unfilteredItems: ArrayList = ArrayList() private var _items: MutableLiveData?> = MutableLiveData?>() From b6d0d58cbecc9ea38d78f723544feb15b841cb23 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 11:31:57 +0200 Subject: [PATCH 63/92] reorder poll category in strings.xml Signed-off-by: Marcel Hibbe --- app/src/main/res/values/strings.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e12eafb77..62afac6ea 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -440,10 +440,6 @@ Play/pause voice message Permission for audio recording is required - - Tap to vote - Tap to see results - phone_book_integration Match contacts based on phone number to integrate Talk shortcut into system contacts app @@ -537,6 +533,8 @@ No search results + Tap to vote + Tap to see results %1$s voters Add Option You successfully voted for this private poll. From bfcab7cb49738a3963435691f3bd7fcd109e902f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 12:00:04 +0200 Subject: [PATCH 64/92] fix copyright Signed-off-by: Marcel Hibbe --- .../polls/viewmodels/PollResultsViewModel.kt | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 205d2a9af..e20b5731d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -2,8 +2,9 @@ * Nextcloud Talk application * * @author Álvaro Brey + * @author Marcel Hibbe * Copyright (C) 2022 Álvaro Brey - * Copyright (C) 2022 Nextcloud GmbH + * Copyright (C) 2022 Marcel Hibbe * * 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 @@ -18,25 +19,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -/* - * Nextcloud Talk application - * - * @author Marcel Hibbe - * Copyright (C) 2022 Marcel Hibbe - * - * 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.polls.viewmodels From 87fdd7017d13a146a5062b0bad37ac4c231d90a3 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 12:04:00 +0200 Subject: [PATCH 65/92] replace @JvmField with lateinit vars Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 13 +++++-------- .../messages/OutcomingPollMessageViewHolder.kt | 13 +++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 4aab36277..ada0de265 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -60,17 +60,14 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH private val binding: ItemCustomIncomingPollMessageBinding = ItemCustomIncomingPollMessageBinding.bind(itemView) - @JvmField @Inject - var context: Context? = null - - @JvmField - @Inject - var appPreferences: AppPreferences? = null + lateinit var context: Context @Inject - @JvmField - var ncApi: NcApi? = null + lateinit var appPreferences: AppPreferences + + @Inject + lateinit var ncApi: NcApi lateinit var message: ChatMessage diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index bcd84bb48..2f9464d3b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -57,17 +57,14 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag private val binding: ItemCustomOutcomingPollMessageBinding = ItemCustomOutcomingPollMessageBinding.bind(itemView) - @JvmField @Inject - var context: Context? = null - - @JvmField - @Inject - var appPreferences: AppPreferences? = null + lateinit var context: Context @Inject - @JvmField - var ncApi: NcApi? = null + lateinit var appPreferences: AppPreferences + + @Inject + lateinit var ncApi: NcApi lateinit var message: ChatMessage From 3267fc2f473f52263c14f0cb205ce0e7f747274e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 12:30:05 +0200 Subject: [PATCH 66/92] remove nullable from PollRepository return types Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/repositories/PollRepository.kt | 8 ++++---- .../talk/polls/repositories/PollRepositoryImpl.kt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt index 1b097968c..feef4b563 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepository.kt @@ -33,11 +33,11 @@ interface PollRepository { options: List, resultMode: Int, maxVotes: Int - ): Observable? + ): Observable - fun getPoll(roomToken: String, pollId: String): Observable? + fun getPoll(roomToken: String, pollId: String): Observable - fun vote(roomToken: String, pollId: String, options: List): Observable? + fun vote(roomToken: String, pollId: String, options: List): Observable - fun closePoll(roomToken: String, pollId: String): Observable? + fun closePoll(roomToken: String, pollId: String): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index fa04db86d..30138b67b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -45,7 +45,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid resultMode: Int, maxVotes: Int - ): Observable? { + ): Observable { return ncApi.createPoll( credentials, ApiUtils.getUrlForPoll( @@ -71,7 +71,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid ).map { mapToPoll(it.ocs?.data!!) } } - override fun vote(roomToken: String, pollId: String, options: List): Observable? { + override fun vote(roomToken: String, pollId: String, options: List): Observable { return ncApi.votePoll( credentials, From 880c656be269bd470f12b3fec014e8dae45531e8 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 12:57:08 +0200 Subject: [PATCH 67/92] enable/disable submit button by liveData Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/ui/PollVoteFragment.kt | 14 +++++--------- .../talk/polls/viewmodels/PollVoteViewModel.kt | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index c8adb9448..2da51b177 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -110,6 +110,10 @@ class PollVoteFragment : Fragment() { } } + viewModel.submitButtonEnabled.observe(viewLifecycleOwner) { enabled -> + binding.pollVoteSubmitButton.isEnabled = enabled + } + binding.pollVoteRadioGroup.setOnCheckedChangeListener { _, checkedId -> viewModel.selectOption(checkedId, true) updateSubmitButton() @@ -174,15 +178,7 @@ class PollVoteFragment : Fragment() { } private fun updateSubmitButton() { - binding.pollVoteSubmitButton.isEnabled = - areSelectedOptionsDifferentToVotedOptions() && viewModel.selectedOptions.isNotEmpty() - } - - private fun areSelectedOptionsDifferentToVotedOptions(): Boolean { - return !( - viewModel.votedOptions.containsAll(viewModel.selectedOptions) && - viewModel.selectedOptions.containsAll(viewModel.votedOptions) - ) + viewModel.updateSubmitButton() } private fun makeOptionBoldIfSelfVoted(button: CompoundButton, poll: Poll, index: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index cb8f16fc2..344915e65 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -45,6 +45,10 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito val viewState: LiveData get() = _viewState + private val _submitButtonEnabled: MutableLiveData = MutableLiveData() + val submitButtonEnabled: LiveData + get() = _submitButtonEnabled + private var disposable: Disposable? = null private var _votedOptions: List = emptyList() @@ -75,7 +79,7 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito fun vote(roomToken: String, pollId: String) { if (_selectedOptions.isNotEmpty()) { repository.vote(roomToken, pollId, _selectedOptions) - ?.doOnSubscribe { disposable = it } + .doOnSubscribe { disposable = it } ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(PollObserver()) @@ -87,6 +91,15 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito disposable?.dispose() } + fun updateSubmitButton() { + val areSelectedOptionsDifferentToVotedOptions = !( + votedOptions.containsAll(selectedOptions) && + selectedOptions.containsAll(votedOptions) + ) + + _submitButtonEnabled.value = areSelectedOptionsDifferentToVotedOptions && selectedOptions.isNotEmpty() + } + inner class PollObserver : Observer { lateinit var poll: Poll From 6dfed0e5e7c976566f845e5989d933ed419807a9 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 13:04:43 +0200 Subject: [PATCH 68/92] avoid unnecessary null checks in PollMessageViewHolders Signed-off-by: Marcel Hibbe --- .../messages/IncomingPollMessageViewHolder.kt | 18 +++++++++--------- .../messages/OutcomingPollMessageViewHolder.kt | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index ada0de265..1f381c65c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -136,7 +136,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH } val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) - ncApi!!.getPoll( + ncApi.getPoll( credentials, ApiUtils.getUrlForPoll( message.activeUser?.baseUrl, @@ -153,10 +153,10 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH override fun onNext(pollOverall: PollOverall) { if (pollOverall.ocs!!.data!!.status == 0) { binding.messagePollSubtitle.text = - context?.resources?.getString(R.string.message_poll_tap_to_vote) + context.resources?.getString(R.string.message_poll_tap_to_vote) } else { binding.messagePollSubtitle.text = - context?.resources?.getString(R.string.message_poll_tap_see_results) + context.resources?.getString(R.string.message_poll_tap_see_results) } } @@ -201,8 +201,8 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH } else if (message.actorType == "bots" && message.actorId == "changelog") { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val layers = arrayOfNulls(2) - layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background) - layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground) + layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background) + layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) val layerDrawable = LayerDrawable(layers) binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable)) } else { @@ -215,7 +215,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH .endConfig() .buildRound( ">", - ResourcesCompat.getColor(context!!.resources, R.color.black, null) + ResourcesCompat.getColor(context.resources, R.color.black, null) ) binding.messageUserAvatar.visibility = View.VISIBLE binding.messageUserAvatar.setImageDrawable(drawable) @@ -248,7 +248,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH if (!message.isDeleted && message.parentMessage != null) { val parentChatMessage = message.parentMessage parentChatMessage!!.activeUser = message.activeUser - parentChatMessage!!.imageUrl?.let { + parentChatMessage.imageUrl?.let { binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE binding.messageQuote.quotedMessageImage.load(it) { addHeader( @@ -260,11 +260,11 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageH binding.messageQuote.quotedMessageImage.visibility = View.GONE } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName - ?: context!!.getText(R.string.nc_nick_guest) + ?: context.getText(R.string.nc_nick_guest) binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessageAuthor - .setTextColor(ContextCompat.getColor(context!!, R.color.textColorMaxContrast)) + .setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast)) if (parentChatMessage.actorId?.equals(message.activeUser!!.userId) == true) { binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.colorPrimary) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index 2f9464d3b..d6c86a239 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -79,7 +79,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag colorizeMessageBubble(message) itemView.isSelected = false - binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60)) + binding.messageTime.setTextColor(context.resources.getColor(R.color.white60)) // parent message handling setParentMessageDataOnMessageItem(message) @@ -97,13 +97,13 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag } readStatusDrawableInt?.let { drawableInt -> - AppCompatResources.getDrawable(context!!, drawableInt)?.let { - it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP) + AppCompatResources.getDrawable(context, drawableInt)?.let { + it.setColorFilter(context.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP) binding.checkMark.setImageDrawable(it) } } - binding.checkMark.setContentDescription(readStatusContentDescriptionString) + binding.checkMark.contentDescription = readStatusContentDescriptionString setPollPreview(message) @@ -152,7 +152,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag } val credentials = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) - ncApi!!.getPoll( + ncApi.getPoll( credentials, ApiUtils.getUrlForPoll( message.activeUser?.baseUrl, @@ -203,12 +203,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag binding.messageQuote.quotedMessageImage.visibility = View.GONE } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName - ?: context!!.getText(R.string.nc_nick_guest) + ?: context.getText(R.string.nc_nick_guest) binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.setTextColor( - context!!.resources.getColor(R.color.nc_outcoming_text_default) + context.resources.getColor(R.color.nc_outcoming_text_default) ) - binding.messageQuote.quotedMessageAuthor.setTextColor(context!!.resources.getColor(R.color.nc_grey)) + binding.messageQuote.quotedMessageAuthor.setTextColor(context.resources.getColor(R.color.nc_grey)) binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white) From 139c4412ad94a5e70f35e0f5e218c203e58f36f1 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 17:04:59 +0200 Subject: [PATCH 69/92] move variables from fragment to PollMainViewModel Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollMainDialogFragment.kt | 46 +++++++++---------- .../polls/viewmodels/PollMainViewModel.kt | 17 ++++--- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 6ca06436f..d1b523ddd 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -27,6 +27,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog +import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector @@ -40,12 +41,6 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollMainDialogFragment : DialogFragment() { - lateinit var user: User - lateinit var roomToken: String - private var isOwnerOrModerator: Boolean = false - lateinit var pollId: String - lateinit var pollTitle: String - @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -58,11 +53,13 @@ class PollMainDialogFragment : DialogFragment() { viewModel = ViewModelProvider(this, viewModelFactory)[PollMainViewModel::class.java] - user = arguments?.getParcelable(KEY_USER_ENTITY)!! - roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! - isOwnerOrModerator = arguments?.getBoolean(KEY_OWNER_OR_MODERATOR)!! - pollId = arguments?.getString(KEY_POLL_ID)!! - pollTitle = arguments?.getString(KEY_POLL_TITLE)!! + val user: User = arguments?.getParcelable(KEY_USER_ENTITY)!! + val roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! + val isOwnerOrModerator = arguments?.getBoolean(KEY_OWNER_OR_MODERATOR)!! + val pollId = arguments?.getString(KEY_POLL_ID)!! + val pollTitle = arguments?.getString(KEY_POLL_TITLE)!! + + viewModel.initialize(user, roomToken, isOwnerOrModerator, pollId, pollTitle) } @SuppressLint("InflateParams") @@ -73,7 +70,7 @@ class PollMainDialogFragment : DialogFragment() { .setView(binding.root) .create() - binding.messagePollTitle.text = pollTitle + binding.messagePollTitle.text = viewModel.pollTitle return dialog } @@ -85,8 +82,6 @@ class PollMainDialogFragment : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.setIsOwnerOrModerator(isOwnerOrModerator) - viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { PollMainViewModel.InitialState -> {} @@ -108,14 +103,12 @@ class PollMainDialogFragment : DialogFragment() { else -> {} } } - - viewModel.initialize(roomToken, pollId) } private fun showVoteScreen() { val contentFragment = PollVoteFragment.newInstance( - roomToken, - pollId + viewModel.roomToken, + viewModel.pollId ) val transaction = childFragmentManager.beginTransaction() @@ -125,7 +118,7 @@ class PollMainDialogFragment : DialogFragment() { private fun showResultsScreen() { val contentFragment = PollResultsFragment.newInstance( - user + viewModel.user ) val transaction = childFragmentManager.beginTransaction() @@ -171,12 +164,15 @@ class PollMainDialogFragment : DialogFragment() { pollId: String, name: String ): PollMainDialogFragment { - val args = Bundle() - args.putParcelable(KEY_USER_ENTITY, user) - args.putString(KEY_ROOM_TOKEN, roomTokenParam) - args.putBoolean(KEY_OWNER_OR_MODERATOR, isOwnerOrModerator) - args.putString(KEY_POLL_ID, pollId) - args.putString(KEY_POLL_TITLE, name) + + val args = bundleOf( + KEY_USER_ENTITY to user, + KEY_ROOM_TOKEN to roomTokenParam, + KEY_OWNER_OR_MODERATOR to isOwnerOrModerator, + KEY_POLL_ID to pollId, + KEY_POLL_TITLE to name + ) + val fragment = PollMainDialogFragment() fragment.arguments = args return fragment diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 2cca1ac87..f9480d6d5 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -24,6 +24,7 @@ import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.repositories.PollRepository import com.nextcloud.talk.utils.database.user.UserUtils @@ -49,10 +50,11 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito @Inject lateinit var userUtils: UserUtils - private lateinit var roomToken: String - private lateinit var pollId: String - + lateinit var user: User + lateinit var roomToken: String private var isOwnerOrModerator: Boolean = false + lateinit var pollId: String + lateinit var pollTitle: String private var editVotes: Boolean = false @@ -84,9 +86,12 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito private var disposable: Disposable? = null - fun initialize(roomToken: String, pollId: String) { + fun initialize(user: User, roomToken: String, isOwnerOrModerator: Boolean, pollId: String, pollTitle: String) { + this.user = user this.roomToken = roomToken + this.isOwnerOrModerator = isOwnerOrModerator this.pollId = pollId + this.pollTitle = pollTitle loadPoll() } @@ -106,7 +111,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito private fun loadPoll() { repository.getPoll(roomToken, pollId) - ?.doOnSubscribe { disposable = it } + .doOnSubscribe { disposable = it } ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(PollObserver()) @@ -114,7 +119,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito fun closePoll() { repository.closePoll(roomToken, pollId) - ?.doOnSubscribe { disposable = it } + .doOnSubscribe { disposable = it } ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(PollObserver()) From dca7e39905c6a4d175dde7464d111fed7429e2b4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 17:12:48 +0200 Subject: [PATCH 70/92] get variables from parentViewModel Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollMainDialogFragment.kt | 9 ++---- .../talk/polls/ui/PollResultsFragment.kt | 26 ++++------------- .../talk/polls/ui/PollVoteFragment.kt | 29 +++---------------- .../polls/viewmodels/PollMainViewModel.kt | 15 ---------- 4 files changed, 11 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index d1b523ddd..e94cc4403 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -106,10 +106,7 @@ class PollMainDialogFragment : DialogFragment() { } private fun showVoteScreen() { - val contentFragment = PollVoteFragment.newInstance( - viewModel.roomToken, - viewModel.pollId - ) + val contentFragment = PollVoteFragment.newInstance() val transaction = childFragmentManager.beginTransaction() transaction.replace(binding.messagePollContentFragment.id, contentFragment) @@ -117,9 +114,7 @@ class PollMainDialogFragment : DialogFragment() { } private fun showResultsScreen() { - val contentFragment = PollResultsFragment.newInstance( - viewModel.user - ) + val contentFragment = PollResultsFragment.newInstance() val transaction = childFragmentManager.beginTransaction() transaction.replace(binding.messagePollContentFragment.id, contentFragment) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 468f26d86..2c48644bd 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -33,7 +33,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import autodagger.AutoInjector import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.DialogPollResultsBinding import com.nextcloud.talk.polls.adapters.PollResultHeaderItem import com.nextcloud.talk.polls.adapters.PollResultItemClickListener @@ -51,8 +50,6 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { private lateinit var parentViewModel: PollMainViewModel lateinit var viewModel: PollResultsViewModel - lateinit var user: User - lateinit var binding: DialogPollResultsBinding private var adapter: PollResultsAdapter? = null @@ -61,12 +58,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollResultsViewModel::class.java] - parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[ - PollMainViewModel::class - .java - ] - - user = arguments?.getParcelable(KEY_USER_ENTITY)!! + parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class.java] } override fun onCreateView( @@ -91,7 +83,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { } viewModel.items.observe(viewLifecycleOwner) { - val adapter = PollResultsAdapter(user, this).apply { + val adapter = PollResultsAdapter(parentViewModel.user, this).apply { if (it != null) { list = it } @@ -101,7 +93,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { } private fun initAdapter() { - adapter = PollResultsAdapter(user, this) + adapter = PollResultsAdapter(parentViewModel.user, this) binding.pollResultsList.adapter = adapter binding.pollResultsList.layoutManager = LinearLayoutManager(context) } @@ -140,17 +132,9 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { } companion object { - private const val KEY_USER_ENTITY = "keyUserEntity" - @JvmStatic - fun newInstance( - user: User - ): PollResultsFragment { - val args = Bundle() - args.putParcelable(KEY_USER_ENTITY, user) - val fragment = PollResultsFragment() - fragment.arguments = args - return fragment + fun newInstance(): PollResultsFragment { + return PollResultsFragment() } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 2da51b177..32688f97c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -47,10 +47,6 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollVoteFragment : Fragment() { - lateinit var roomToken: String - - lateinit var pollId: String - @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -64,13 +60,7 @@ class PollVoteFragment : Fragment() { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollVoteViewModel::class.java] - parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[ - PollMainViewModel::class - .java - ] - - roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! - pollId = arguments?.getString(KEY_POLL_ID)!! + parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class.java] } override fun onCreateView( @@ -120,7 +110,7 @@ class PollVoteFragment : Fragment() { } binding.pollVoteSubmitButton.setOnClickListener { - viewModel.vote(roomToken, pollId) + viewModel.vote(parentViewModel.roomToken, parentViewModel.pollId) } binding.pollVoteEditDismiss.setOnClickListener { @@ -209,20 +199,9 @@ class PollVoteFragment : Fragment() { private val TAG = PollVoteFragment::class.java.simpleName private const val UNLIMITED_VOTES = 0 - private const val KEY_ROOM_TOKEN = "keyRoomToken" - private const val KEY_POLL_ID = "keyPollId" - @JvmStatic - fun newInstance( - roomTokenParam: String, - pollId: String - ): PollVoteFragment { - val args = Bundle() - args.putString(KEY_ROOM_TOKEN, roomTokenParam) - args.putString(KEY_POLL_ID, pollId) - val fragment = PollVoteFragment() - fragment.arguments = args - return fragment + fun newInstance(): PollVoteFragment { + return PollVoteFragment() } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index f9480d6d5..e7d89fc6c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -34,17 +34,6 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -/** - * @startuml - * hide empty description - * [*] --> InitialState - * InitialState --> PollOpenState - * note left - * Open second viewmodel for child fragment - * end note - * InitialState --> PollClosedState - * @enduml - */ class PollMainViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { @Inject @@ -190,10 +179,6 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito return userUtils.currentUser?.userId == poll.actorId } - fun setIsOwnerOrModerator(ownerOrModerator: Boolean) { - isOwnerOrModerator = ownerOrModerator - } - companion object { private val TAG = PollMainViewModel::class.java.simpleName } From cd3f8af24a1b786ca1d5ed357cee367912a221d0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 19 Jul 2022 17:20:19 +0200 Subject: [PATCH 71/92] change strings Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_poll_results.xml | 2 +- app/src/main/res/layout/dialog_poll_vote.xml | 2 +- app/src/main/res/values/strings.xml | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 8c488b818..1e845afe7 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -56,7 +56,7 @@ android:id="@+id/edit_vote_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/edit" + android:text="@string/polls_edit_vote" android:theme="@style/Button.Primary" app:cornerRadius="@dimen/button_corner_radius" /> diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index b3cc0ddb4..c37eec3e2 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -81,7 +81,7 @@ android:id="@+id/poll_vote_submit_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/nc_common_submit" + android:text="@string/polls_submit_vote" android:theme="@style/Button.Primary" app:cornerRadius="@dimen/button_corner_radius" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62afac6ea..b25329f57 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,7 +28,6 @@ Set Dismiss Sorry, something went wrong! - Submit Settings @@ -535,10 +534,12 @@ Tap to vote Tap to see results - %1$s voters - Add Option + %1$s votes + Add option You successfully voted for this private poll. - End Poll + Edit vote + Vote + End poll Do you really want to end this poll? This can\'t be undone. You can\'t vote with more options for this poll. Results From ca0c045e8d67b69db3e595d980620629aaab4740 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 20 Jul 2022 10:25:19 +0200 Subject: [PATCH 72/92] initialize viewModel in onCreate Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/ui/PollCreateDialogFragment.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index d0ccefeb5..3a397aa72 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -47,8 +47,6 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener { - lateinit var roomToken: String - @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -62,8 +60,8 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) viewModel = ViewModelProvider(this, viewModelFactory)[PollCreateViewModel::class.java] - - roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! + val roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! + viewModel.initialize(roomToken) } @SuppressLint("InflateParams") @@ -89,8 +87,6 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener adapter = PollCreateOptionsAdapter(this) binding.pollCreateOptionsList.adapter = adapter - viewModel.initialize(roomToken) - setupListeners() setupStateObserver() } From ba6cc7170bcb9dfc13487691574faf343cca0a2a Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 20 Jul 2022 13:10:20 +0200 Subject: [PATCH 73/92] initially add 2 option fields and focus question field Signed-off-by: Marcel Hibbe --- .../polls/adapters/PollCreateOptionViewHolder.kt | 2 +- .../polls/adapters/PollCreateOptionsItemListener.kt | 4 ++++ .../talk/polls/ui/PollCreateDialogFragment.kt | 12 ++++++++++++ .../talk/polls/viewmodels/PollCreateViewModel.kt | 2 +- app/src/main/res/layout/dialog_poll_create.xml | 6 ++++-- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index edb223103..6d40969dc 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -49,7 +49,7 @@ class PollCreateOptionViewHolder( binding.pollOptionText.setText(pollCreateOptionItem.pollOption) if (focus) { - binding.pollOptionText.requestFocus() + itemsListener.requestFocus(binding.pollOptionText) } binding.pollOptionDelete.setOnClickListener { diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt index 30c83fc9a..a057592bd 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt @@ -20,9 +20,13 @@ package com.nextcloud.talk.polls.adapters +import android.widget.EditText + interface PollCreateOptionsItemListener { fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) fun onOptionsItemTextChanged(pollCreateOptionItem: PollCreateOptionItem) + + fun requestFocus(textField: EditText) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 3a397aa72..2333d3313 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -29,6 +29,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.EditText import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment @@ -89,6 +90,9 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener setupListeners() setupStateObserver() + + viewModel.addOption() + viewModel.addOption() } private fun setupListeners() { @@ -159,6 +163,14 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener viewModel.optionsItemTextChanged() } + override fun requestFocus(textField: EditText) { + if (binding.pollCreateQuestion.text.isBlank()) { + binding.pollCreateQuestion.requestFocus() + } else { + textField.requestFocus() + } + } + /** * Fragment creator */ diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index fcb38635c..e78184b3e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -113,7 +113,7 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi roomToken, _question, _options.value!!.map { it.pollOption }, resultMode, maxVotes ) - ?.doOnSubscribe { disposable = it } + .doOnSubscribe { disposable = it } ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(PollObserver()) diff --git a/app/src/main/res/layout/dialog_poll_create.xml b/app/src/main/res/layout/dialog_poll_create.xml index d1d1b73a1..9f954a325 100644 --- a/app/src/main/res/layout/dialog_poll_create.xml +++ b/app/src/main/res/layout/dialog_poll_create.xml @@ -33,10 +33,12 @@ android:textStyle="bold" android:text="@string/polls_question" /> - + android:layout_height="wrap_content" + android:inputType="text" + tools:ignore="Autofill,LabelFor"/> Date: Wed, 20 Jul 2022 13:48:32 +0200 Subject: [PATCH 74/92] add Options in init method this avoids adding them again on screen rotation... Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/polls/ui/PollCreateDialogFragment.kt | 5 +---- .../com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt | 2 +- .../nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt | 7 ++++++- .../nextcloud/talk/polls/viewmodels/PollMainViewModel.kt | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 2333d3313..cdaa8fa58 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -62,7 +62,7 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener viewModel = ViewModelProvider(this, viewModelFactory)[PollCreateViewModel::class.java] val roomToken = arguments?.getString(KEY_ROOM_TOKEN)!! - viewModel.initialize(roomToken) + viewModel.setData(roomToken) } @SuppressLint("InflateParams") @@ -90,9 +90,6 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener setupListeners() setupStateObserver() - - viewModel.addOption() - viewModel.addOption() } private fun setupListeners() { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index e94cc4403..35af29b2e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -59,7 +59,7 @@ class PollMainDialogFragment : DialogFragment() { val pollId = arguments?.getString(KEY_POLL_ID)!! val pollTitle = arguments?.getString(KEY_POLL_TITLE)!! - viewModel.initialize(user, roomToken, isOwnerOrModerator, pollId, pollTitle) + viewModel.setData(user, roomToken, isOwnerOrModerator, pollId, pollTitle) } @SuppressLint("InflateParams") diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index e78184b3e..269f579b0 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -70,7 +70,12 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi private var disposable: Disposable? = null - fun initialize(roomToken: String) { + init { + addOption() + addOption() + } + + fun setData(roomToken: String) { this.roomToken = roomToken updateCreationState() } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index e7d89fc6c..48534eeb4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -75,7 +75,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito private var disposable: Disposable? = null - fun initialize(user: User, roomToken: String, isOwnerOrModerator: Boolean, pollId: String, pollTitle: String) { + fun setData(user: User, roomToken: String, isOwnerOrModerator: Boolean, pollId: String, pollTitle: String) { this.user = user this.roomToken = roomToken this.isOwnerOrModerator = isOwnerOrModerator From fe217d97007fc0426db67960c13679b7aafdd8fc Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 20 Jul 2022 14:12:17 +0200 Subject: [PATCH 75/92] scroll complete poll create screen Signed-off-by: Marcel Hibbe --- .../main/res/layout/dialog_poll_create.xml | 154 +++++++++--------- 1 file changed, 75 insertions(+), 79 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_create.xml b/app/src/main/res/layout/dialog_poll_create.xml index 9f954a325..46c80f3e8 100644 --- a/app/src/main/res/layout/dialog_poll_create.xml +++ b/app/src/main/res/layout/dialog_poll_create.xml @@ -17,7 +17,8 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - - - - - - + + + + + + - - - - - - - - - - - + android:text="@string/polls_add_option" /> - + android:textColor="@color/colorPrimary" + android:textStyle="bold" + android:layout_marginTop="@dimen/standard_margin" + android:layout_marginBottom="@dimen/standard_half_margin" + android:text="@string/polls_settings" /> + + + + + + + + + + + - - + From f46d2d28f509e15ced95104bd40859ab0affe2ff Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 20 Jul 2022 17:26:26 +0200 Subject: [PATCH 76/92] close dialog when voted for hidden poll Signed-off-by: Marcel Hibbe --- .../talk/polls/ui/PollMainDialogFragment.kt | 10 +++------- .../com/nextcloud/talk/polls/ui/PollVoteFragment.kt | 8 ++++---- .../talk/polls/viewmodels/PollMainViewModel.kt | 13 ++++++------- .../talk/polls/viewmodels/PollVoteViewModel.kt | 9 +++++++-- app/src/main/res/layout/dialog_poll_main.xml | 10 ---------- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 21 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index 35af29b2e..c432ff9bd 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -85,21 +85,17 @@ class PollMainDialogFragment : DialogFragment() { viewModel.viewState.observe(viewLifecycleOwner) { state -> when (state) { PollMainViewModel.InitialState -> {} - is PollMainViewModel.PollVoteHiddenState -> { - binding.pollVotedHidden.visibility = View.VISIBLE - initVotersAmount(state.showVotersAmount, state.poll.numVoters, false) - showVoteScreen() - } is PollMainViewModel.PollVoteState -> { - binding.pollVotedHidden.visibility = View.GONE initVotersAmount(state.showVotersAmount, state.poll.numVoters, false) showVoteScreen() } is PollMainViewModel.PollResultState -> { - binding.pollVotedHidden.visibility = View.GONE initVotersAmount(state.showVotersAmount, state.poll.numVoters, true) showResultsScreen() } + is PollMainViewModel.DismissDialogState -> { + dismiss() + } else -> {} } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 32688f97c..e8286de1c 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -80,10 +80,6 @@ class PollVoteFragment : Fragment() { initEndPollButton(state.showEndPollButton) updateSubmitButton() updateDismissEditButton(state.showDismissEditButton) - } else if (state is PollMainViewModel.PollVoteHiddenState) { - initPollOptions(state.poll) - initEndPollButton(state.showEndPollButton) - updateSubmitButton() } } @@ -94,6 +90,10 @@ class PollVoteFragment : Fragment() { Log.e(TAG, "Failed to vote on poll.") Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show() } + is PollVoteViewModel.PollVoteHiddenSuccessState -> { + Toast.makeText(context, R.string.polls_voted_hidden_success, Toast.LENGTH_LONG).show() + parentViewModel.dismissDialog() + } is PollVoteViewModel.PollVoteSuccessState -> { parentViewModel.voted() } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index 48534eeb4..d5fdbf308 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -49,6 +49,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito sealed interface ViewState object InitialState : ViewState + object DismissDialogState : ViewState open class PollVoteState( val poll: Poll, val showVotersAmount: Boolean, @@ -56,12 +57,6 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito val showDismissEditButton: Boolean ) : ViewState - open class PollVoteHiddenState( - val poll: Poll, - val showVotersAmount: Boolean, - val showEndPollButton: Boolean - ) : ViewState - open class PollResultState( val poll: Poll, val showVotersAmount: Boolean, @@ -138,7 +133,7 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito val showVotersAmount = showVotersAmount(poll) if (votedForOpenHiddenPoll(poll)) { - _viewState.value = PollVoteHiddenState(poll, showVotersAmount, showEndPollButton) + _viewState.value = PollVoteState(poll, showVotersAmount, showEndPollButton, false) } else if (editVotes && poll.status == Poll.STATUS_OPEN) { _viewState.value = PollVoteState(poll, false, showEndPollButton, true) editVotes = false @@ -179,6 +174,10 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito return userUtils.currentUser?.userId == poll.actorId } + fun dismissDialog() { + _viewState.value = DismissDialogState + } + companion object { private val TAG = PollMainViewModel::class.java.simpleName } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 344915e65..a13ea3639 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -38,7 +38,8 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito sealed interface ViewState object InitialState : ViewState - open class PollVoteSuccessState(val poll: Poll) : ViewState + open class PollVoteSuccessState : ViewState + open class PollVoteHiddenSuccessState : ViewState open class PollVoteFailedState : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) @@ -116,7 +117,11 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito } override fun onComplete() { - _viewState.value = PollVoteSuccessState(poll) + if (poll.resultMode == 1) { + _viewState.value = PollVoteHiddenSuccessState() + } else { + _viewState.value = PollVoteSuccessState() + } } } diff --git a/app/src/main/res/layout/dialog_poll_main.xml b/app/src/main/res/layout/dialog_poll_main.xml index 70f40d028..d583b2905 100644 --- a/app/src/main/res/layout/dialog_poll_main.xml +++ b/app/src/main/res/layout/dialog_poll_main.xml @@ -85,16 +85,6 @@ - - - - Tap to see results %1$s votes Add option - You successfully voted for this private poll. Edit vote Vote + Successfully voted End poll Do you really want to end this poll? This can\'t be undone. You can\'t vote with more options for this poll. From 58739b8f1485bf00e166b226686179aeb91c76b0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 20 Jul 2022 17:49:18 +0200 Subject: [PATCH 77/92] disable buttons while waiting for response avoids doubleclick and doubled data Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt | 2 ++ .../com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index 269f579b0..ebaae84be 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -114,6 +114,8 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi _options.value = _options.value?.filter { it.pollOption.isNotEmpty() } as ArrayList if (_question.isNotEmpty() && _options.value?.isNotEmpty() == true) { + _viewState.value = PollCreationState(enableAddOptionButton = false, enableCreatePollButton = false) + repository.createPoll( roomToken, _question, _options.value!!.map { it.pollOption }, resultMode, maxVotes diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index a13ea3639..e3689ff62 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -79,6 +79,8 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito fun vote(roomToken: String, pollId: String) { if (_selectedOptions.isNotEmpty()) { + _submitButtonEnabled.value = false + repository.vote(roomToken, pollId, _selectedOptions) .doOnSubscribe { disposable = it } ?.subscribeOn(Schedulers.io()) From c7014fd063386c06b7e69517ac2ab751134339bd Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 20 Jul 2022 18:10:12 +0200 Subject: [PATCH 78/92] hide system message "You voted on the poll ..." Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/controllers/ChatController.kt | 9 +++++++++ 1 file changed, 9 insertions(+) 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 d271fc8dc..48a7fc96e 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -2593,6 +2593,11 @@ class ChatController(args: Bundle) : chatMessageIterator.remove() } + + // delete poll system messages + else if (isPollVotedMessage(currentMessage)) { + chatMessageIterator.remove() + } } return chatMessageMap.values.toList() } @@ -2608,6 +2613,10 @@ class ChatController(args: Bundle) : currentMessage.value.systemMessageType == ChatMessage.SystemMessageType.REACTION_REVOKED } + private fun isPollVotedMessage(currentMessage: MutableMap.MutableEntry): Boolean { + return currentMessage.value.systemMessageType == ChatMessage.SystemMessageType.POLL_VOTED + } + private fun startACall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean) { if (currentConversation?.canStartCall == false && currentConversation?.hasCall == false) { Toast.makeText(context, R.string.startCallForbidden, Toast.LENGTH_LONG).show() From 20dabf3a724e89116abf52387b05e989afae687b Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 20 Jul 2022 20:11:02 +0200 Subject: [PATCH 79/92] wip. toggle between voters overview and voters details Signed-off-by: Marcel Hibbe --- .../adapters/PollResultVotersOverviewItem.kt | 38 ++++++ .../PollResultVotersOverviewViewHolder.kt | 109 ++++++++++++++++++ .../talk/polls/adapters/PollResultsAdapter.kt | 12 ++ .../talk/polls/ui/PollResultsFragment.kt | 2 +- .../polls/viewmodels/PollResultsViewModel.kt | 33 +++--- .../poll_result_voters_overview_item.xml | 32 +++++ 6 files changed, 208 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewItem.kt create mode 100644 app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt create mode 100644 app/src/main/res/layout/poll_result_voters_overview_item.xml diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewItem.kt new file mode 100644 index 000000000..ffb3065f3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewItem.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters + +import com.nextcloud.talk.R +import com.nextcloud.talk.polls.model.PollDetails + +data class PollResultVotersOverviewItem( + val detailsList: List +) : PollResultItem { + + override fun getViewType(): Int { + return VIEW_TYPE + } + + companion object { + // layout is used as view type for uniqueness + const val VIEW_TYPE: Int = R.layout.poll_result_voters_overview_item + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt new file mode 100644 index 000000000..c421ef80b --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt @@ -0,0 +1,109 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * 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.polls.adapters + +import android.annotation.SuppressLint +import android.text.TextUtils +import android.widget.LinearLayout +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.generic.RoundingParams +import com.facebook.drawee.interfaces.DraweeController +import com.facebook.drawee.view.SimpleDraweeView +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.databinding.PollResultVotersOverviewItemBinding +import com.nextcloud.talk.polls.model.PollDetails +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DisplayUtils + +class PollResultVotersOverviewViewHolder( + private val user: User, + override val binding: PollResultVotersOverviewItemBinding +) : PollResultViewHolder(binding) { + + @SuppressLint("SetTextI18n") + override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { + val item = pollResultItem as PollResultVotersOverviewItem + + val lp = LinearLayout.LayoutParams( + AVATAR_WIDTH, + AVATAR_HEIGHT + ) + + item.detailsList.forEach { + val avatar = SimpleDraweeView(binding.root.context) + avatar.layoutParams = lp + + val roundingParams = RoundingParams.fromCornersRadius(AVATAR_RADIUS) + roundingParams.roundAsCircle = true + + avatar.hierarchy.roundingParams = roundingParams + avatar.controller = getAvatarDraweeController(it) + + binding.votersAvatarsOverviewWrapper.addView(avatar) + } + } + + private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? { + var draweeController: DraweeController? = null + if (pollDetail.actorType == "guests") { + var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest) + if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { + displayName = pollDetail.actorDisplayName!! + } + draweeController = Fresco.newDraweeControllerBuilder() + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForGuestAvatar( + user.baseUrl, + displayName, + false + ), + user + ) + ) + .build() + } else if (pollDetail.actorType == "users") { + draweeController = Fresco.newDraweeControllerBuilder() + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatar( + user.baseUrl, + pollDetail.actorId, + false + ), + user + ) + ) + .build() + } + return draweeController + } + + companion object { + const val AVATAR_WIDTH = 90 + const val AVATAR_HEIGHT = 70 + const val AVATAR_RADIUS = 5f + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index 9fa3b1dc2..98a576cba 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -26,6 +26,7 @@ import androidx.recyclerview.widget.RecyclerView import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.PollResultHeaderItemBinding import com.nextcloud.talk.databinding.PollResultVoterItemBinding +import com.nextcloud.talk.databinding.PollResultVotersOverviewItemBinding class PollResultsAdapter( private val user: User, @@ -51,6 +52,13 @@ class PollResultsAdapter( ) viewHolder = PollResultVoterViewHolder(user, itemBinding) } + PollResultVotersOverviewItem.VIEW_TYPE -> { + val itemBinding = PollResultVotersOverviewItemBinding.inflate( + LayoutInflater.from(parent.context), parent, + false + ) + viewHolder = PollResultVotersOverviewViewHolder(user, itemBinding) + } } return viewHolder!! } @@ -65,6 +73,10 @@ class PollResultsAdapter( val pollResultItem = list[position] holder.bind(pollResultItem as PollResultVoterItem, clickListener) } + PollResultVotersOverviewItem.VIEW_TYPE -> { + val pollResultItem = list[position] + holder.bind(pollResultItem as PollResultVotersOverviewItem, clickListener) + } } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 2c48644bd..578a7d648 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -128,7 +128,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { } override fun onClick(pollResultHeaderItem: PollResultHeaderItem) { - viewModel.filterItems() + viewModel.toggleDetails() } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index e20b5731d..015341016 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModel import com.nextcloud.talk.polls.adapters.PollResultHeaderItem import com.nextcloud.talk.polls.adapters.PollResultItem import com.nextcloud.talk.polls.adapters.PollResultVoterItem +import com.nextcloud.talk.polls.adapters.PollResultVotersOverviewItem import com.nextcloud.talk.polls.model.Poll import io.reactivex.disposables.Disposable import javax.inject.Inject @@ -40,7 +41,8 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { val poll: Poll? get() = _poll - private var _unfilteredItems: ArrayList = ArrayList() + private var _itemsOverviewList: ArrayList = ArrayList() + private var _itemsDetailsList: ArrayList = ArrayList() private var _items: MutableLiveData?> = MutableLiveData?>() val items: MutableLiveData?> @@ -72,23 +74,23 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { optionsPercent, isOptionSelfVoted(poll, index) ) - addToItems(pollResultHeaderItem) + _itemsOverviewList.add(pollResultHeaderItem) + _itemsDetailsList.add(pollResultHeaderItem) val voters = poll.details?.filter { it.optionId == index } + + if (!voters.isNullOrEmpty()) { + _itemsOverviewList.add(PollResultVotersOverviewItem(voters)) + } + if (!voters.isNullOrEmpty()) { voters.forEach { - addToItems(PollResultVoterItem(it)) + _itemsDetailsList.add(PollResultVoterItem(it)) } } } - _unfilteredItems = _items.value?.let { ArrayList(it) }!! - } - - private fun addToItems(pollResultItem: PollResultItem) { - val tempList = _items.value - tempList!!.add(pollResultItem) - _items.value = tempList + _items.value = _itemsOverviewList } private fun getVotersAmountForOption(poll: Poll, index: Int): Int { @@ -108,14 +110,11 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { return poll.votedSelf?.contains(index) == true } - fun filterItems() { - if (_items.value?.containsAll(_unfilteredItems) == true) { - val filteredList = _items.value?.filter { it.getViewType() == PollResultHeaderItem.VIEW_TYPE } as - MutableList - - _items.value = ArrayList(filteredList) + fun toggleDetails() { + if (_items.value?.containsAll(_itemsDetailsList) == true) { + _items.value = _itemsOverviewList } else { - _items.value = _unfilteredItems + _items.value = _itemsDetailsList } } diff --git a/app/src/main/res/layout/poll_result_voters_overview_item.xml b/app/src/main/res/layout/poll_result_voters_overview_item.xml new file mode 100644 index 000000000..de5542cc2 --- /dev/null +++ b/app/src/main/res/layout/poll_result_voters_overview_item.xml @@ -0,0 +1,32 @@ + + + + + From 64412a187683bd5f09dfd2f9d19e3b58fb12daf7 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 22 Jul 2022 09:07:27 +0200 Subject: [PATCH 80/92] toggle details view on click on avatar views Signed-off-by: Marcel Hibbe --- .../talk/polls/adapters/PollResultHeaderViewHolder.kt | 2 +- .../talk/polls/adapters/PollResultItemClickListener.kt | 2 +- .../nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt | 3 +++ .../talk/polls/adapters/PollResultVotersOverviewViewHolder.kt | 2 ++ .../java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt | 3 +-- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt index 6f780e64c..b963f515d 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt @@ -32,7 +32,7 @@ class PollResultHeaderViewHolder( override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { val item = pollResultItem as PollResultHeaderItem - binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } + binding.root.setOnClickListener { clickListener.onClick() } binding.pollOptionText.text = item.name binding.pollOptionPercentText.text = "${item.percent}%" diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt index f5e5b1267..d0a15156e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt @@ -21,5 +21,5 @@ package com.nextcloud.talk.polls.adapters interface PollResultItemClickListener { - fun onClick(pollResultHeaderItem: PollResultHeaderItem) + fun onClick() } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt index 756887c4b..543591302 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt @@ -40,6 +40,9 @@ class PollResultVoterViewHolder( @SuppressLint("SetTextI18n") override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { val item = pollResultItem as PollResultVoterItem + + binding.root.setOnClickListener { clickListener.onClick() } + binding.pollVoterName.text = item.details.actorDisplayName binding.pollVoterAvatar.controller = getAvatarDraweeController(item.details) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt index c421ef80b..8e2f0206a 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt @@ -44,6 +44,8 @@ class PollResultVotersOverviewViewHolder( override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { val item = pollResultItem as PollResultVotersOverviewItem + binding.root.setOnClickListener { clickListener.onClick() } + val lp = LinearLayout.LayoutParams( AVATAR_WIDTH, AVATAR_HEIGHT diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 578a7d648..8945483e4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -34,7 +34,6 @@ import autodagger.AutoInjector import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollResultsBinding -import com.nextcloud.talk.polls.adapters.PollResultHeaderItem import com.nextcloud.talk.polls.adapters.PollResultItemClickListener import com.nextcloud.talk.polls.adapters.PollResultsAdapter import com.nextcloud.talk.polls.viewmodels.PollMainViewModel @@ -127,7 +126,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { } } - override fun onClick(pollResultHeaderItem: PollResultHeaderItem) { + override fun onClick() { viewModel.toggleDetails() } From b2e47d885c740e2c80c9f654d46163d42af06def Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 22 Jul 2022 12:13:26 +0200 Subject: [PATCH 81/92] show overlapped mini avatars in results overview Signed-off-by: Marcel Hibbe --- .../PollResultVotersOverviewViewHolder.kt | 46 +++++++++++++++---- .../res/layout/poll_result_voter_item.xml | 2 - .../poll_result_voters_overview_item.xml | 5 +- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt index 8e2f0206a..c3805afe1 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt @@ -23,6 +23,8 @@ package com.nextcloud.talk.polls.adapters import android.annotation.SuppressLint import android.text.TextUtils import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.res.ResourcesCompat import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.generic.RoundingParams import com.facebook.drawee.interfaces.DraweeController @@ -46,22 +48,47 @@ class PollResultVotersOverviewViewHolder( binding.root.setOnClickListener { clickListener.onClick() } - val lp = LinearLayout.LayoutParams( - AVATAR_WIDTH, - AVATAR_HEIGHT + val layoutParams = LinearLayout.LayoutParams( + AVATAR_SIZE, + AVATAR_SIZE ) - item.detailsList.forEach { + var avatarsToDisplay = MAX_AVATARS + if (item.detailsList.size < avatarsToDisplay) { + avatarsToDisplay = item.detailsList.size + } + val shotsDots = item.detailsList.size > avatarsToDisplay + + for (i in 0 until avatarsToDisplay) { + val pollDetails = item.detailsList[i] val avatar = SimpleDraweeView(binding.root.context) - avatar.layoutParams = lp + + layoutParams.marginStart = i * AVATAR_OFFSET + avatar.layoutParams = layoutParams + + avatar.translationZ = i.toFloat() * -1 val roundingParams = RoundingParams.fromCornersRadius(AVATAR_RADIUS) roundingParams.roundAsCircle = true + roundingParams.borderColor = ResourcesCompat.getColor( + itemView.context.resources!!, + R.color.colorPrimary, + null + ) + roundingParams.borderWidth = 2.0f avatar.hierarchy.roundingParams = roundingParams - avatar.controller = getAvatarDraweeController(it) + avatar.controller = getAvatarDraweeController(pollDetails) binding.votersAvatarsOverviewWrapper.addView(avatar) + + if (i == avatarsToDisplay - 1 && shotsDots) { + val dotsView = TextView(itemView.context) + layoutParams.marginStart = i * AVATAR_OFFSET + DOTS_OFFSET + dotsView.layoutParams = layoutParams + dotsView.text = DOTS_TEXT + binding.votersAvatarsOverviewWrapper.addView(dotsView) + } } } @@ -104,8 +131,11 @@ class PollResultVotersOverviewViewHolder( } companion object { - const val AVATAR_WIDTH = 90 - const val AVATAR_HEIGHT = 70 + const val AVATAR_SIZE = 60 const val AVATAR_RADIUS = 5f + const val MAX_AVATARS = 10 + const val AVATAR_OFFSET = AVATAR_SIZE - 10 + const val DOTS_OFFSET = 70 + const val DOTS_TEXT = "…" } } diff --git a/app/src/main/res/layout/poll_result_voter_item.xml b/app/src/main/res/layout/poll_result_voter_item.xml index 149a05271..7979a3e2d 100644 --- a/app/src/main/res/layout/poll_result_voter_item.xml +++ b/app/src/main/res/layout/poll_result_voter_item.xml @@ -23,8 +23,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="@dimen/standard_half_padding" - android:paddingEnd="@dimen/standard_half_padding" android:paddingBottom="4dp" tools:background="@color/white"> diff --git a/app/src/main/res/layout/poll_result_voters_overview_item.xml b/app/src/main/res/layout/poll_result_voters_overview_item.xml index de5542cc2..34f1266b2 100644 --- a/app/src/main/res/layout/poll_result_voters_overview_item.xml +++ b/app/src/main/res/layout/poll_result_voters_overview_item.xml @@ -18,7 +18,7 @@ ~ along with this program. If not, see . --> - - - + From 78274472e15ed2e5d8df905e475ae1565add1fd9 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 22 Jul 2022 13:37:51 +0200 Subject: [PATCH 82/92] more whitespace between results list and buttons on bottom Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_poll_results.xml | 1 + app/src/main/res/layout/dialog_poll_vote.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 1e845afe7..0e347c4a5 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -41,6 +41,7 @@ Date: Fri, 22 Jul 2022 14:04:44 +0200 Subject: [PATCH 83/92] adjust minor design (move pixels) Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_poll_vote.xml | 15 +++++++-------- .../main/res/layout/poll_result_header_item.xml | 2 -- .../layout/poll_result_voters_overview_item.xml | 2 -- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_vote.xml b/app/src/main/res/layout/dialog_poll_vote.xml index 6b2762f91..c69f0a946 100644 --- a/app/src/main/res/layout/dialog_poll_vote.xml +++ b/app/src/main/res/layout/dialog_poll_vote.xml @@ -19,18 +19,17 @@ --> + android:orientation="vertical" + tools:background="@color/white"> + android:layout_weight="1"> + android:layout_marginStart="-4dp" + tools:layout_height="400dp" /> diff --git a/app/src/main/res/layout/poll_result_header_item.xml b/app/src/main/res/layout/poll_result_header_item.xml index 393bf48d5..44bfbf364 100644 --- a/app/src/main/res/layout/poll_result_header_item.xml +++ b/app/src/main/res/layout/poll_result_header_item.xml @@ -23,8 +23,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="@dimen/standard_half_padding" - android:paddingEnd="@dimen/standard_half_padding" tools:background="@color/white"> From 8f3a18655e385bfc078d5e481528fa7d4e2093ff Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 22 Jul 2022 14:54:01 +0200 Subject: [PATCH 84/92] change button heights (support multiple rows) layout_gravity=center_vertical was added that buttons with 1 row look identical to buttons with 2 rows Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/dialog_poll_results.xml | 4 ++-- app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/styles.xml | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 0e347c4a5..5b11ddd61 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -47,7 +47,7 @@ diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index e4dc99eb7..2e7c81ded 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -72,4 +72,5 @@ 2dp 50dp + 58dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 02455e83e..011181880 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -147,6 +147,7 @@ @color/white sans bold + center_vertical