add data classes for polls (WIP)

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2022-05-24 15:31:16 +02:00 committed by Andy Scherzinger (Rebase PR Action)
parent 12efda5064
commit f2025332ab
13 changed files with 854 additions and 19 deletions

View File

@ -0,0 +1,241 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.adapters.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<ChatMessage>(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<String?, String?> = 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<Drawable>(2)
layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background)
layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground)
val layerDrawable = LayerDrawable(layers)
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
}
}

View File

@ -0,0 +1,175 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.adapters.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<ChatMessage>(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
}
}

View File

@ -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<PollOverall> getPoll(@Header("Authorization") String authorization,
@Url String url);
@POST
Observable<PollOverall> createPoll(@Header("Authorization") String authorization,
@Url String url);
@POST
Observable<PollOverall> votePoll(@Header("Authorization") String authorization,
@Url String url);
@DELETE
Observable<PollOverall> closePoll(@Header("Authorization") String authorization,
@Url String url);
}

View File

@ -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

View File

@ -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
}

View File

@ -0,0 +1,65 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.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<String>? = null,
@JsonField(name = ["votes"])
var votes: ArrayList<Int>? = 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<Int>? = 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)
}

View File

@ -0,0 +1,35 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.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)
}

View File

@ -0,0 +1,35 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.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)
}

View File

@ -2,8 +2,10 @@
* Nextcloud Talk application
*
* @author Mario Danic
* @author Marcel Hibbe
* @author Tim Krüger
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2021-2022 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* 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,6 +297,7 @@ 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;
}
@ -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;
}
}

View File

@ -0,0 +1,10 @@
<vector android:height="24dp"
android:tint="#000000"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M5,9.2h3L8,19L5,19zM10.6,5h2.8v14h-2.8zM16.2,13L19,13v6h-2.8z" />
</vector>

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud Talk application
~
~ @author Marcel Hibbe
~ Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="2dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="2dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@id/messageUserAvatar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentTop="true"
android:layout_marginEnd="8dp"
app:roundAsCircle="true" />
<com.google.android.flexbox.FlexboxLayout
android:id="@id/bubble"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/message_incoming_bubble_margin_right"
android:layout_toEndOf="@id/messageUserAvatar"
android:orientation="vertical"
app:alignContent="stretch"
app:alignItems="stretch"
app:flexWrap="wrap"
app:justifyContent="flex_end">
<include
android:id="@+id/message_quote"
layout="@layout/item_message_quote"
android:visibility="gone" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/messageAuthor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:textAlignment="viewStart"
android:textColor="@color/textColorMaxContrast"
android:textSize="12sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/message_poll_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_bar_chart_24"
app:tint="@color/high_emphasis_menu_icon"></ImageView>
<androidx.emoji.widget.EmojiTextView
android:id="@+id/message_poll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:textStyle="bold"
tools:text="This is the poll title?" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/double_margin_between_elements"
android:text="@string/message_poll_tap_to_vote"></TextView>
<TextView
android:id="@id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageText"
android:layout_marginStart="8dp"
app:layout_alignSelf="center"
tools:text="12:38" />
<include
android:id="@+id/reactions"
layout="@layout/reactions_inside_message" />
</com.google.android.flexbox.FlexboxLayout>
</RelativeLayout>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud Talk application
~
~ @author Marcel Hibbe
~ Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="2dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="2dp">
<com.google.android.flexbox.FlexboxLayout
android:id="@id/bubble"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/message_outcoming_bubble_margin_left"
app:alignContent="stretch"
app:alignItems="stretch"
app:flexWrap="wrap"
app:justifyContent="flex_end">
<include
android:id="@+id/message_quote"
layout="@layout/item_message_quote"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
</LinearLayout>
<TextView
android:id="@id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageText"
android:layout_marginStart="8dp"
app:layout_alignSelf="center"
tools:text="10:35" />
<ImageView
android:id="@+id/checkMark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageTime"
android:layout_marginStart="8dp"
app:layout_alignSelf="center"
android:contentDescription="@null" />
<include
android:id="@+id/reactions"
layout="@layout/reactions_inside_message" />
</com.google.android.flexbox.FlexboxLayout>
</RelativeLayout>

View File

@ -309,6 +309,7 @@
<string name="nc_sent_an_audio" formatted="true">%1$s sent an audio.</string>
<string name="nc_sent_a_video" formatted="true">%1$s sent a video.</string>
<string name="nc_sent_an_image" formatted="true">%1$s sent an image.</string>
<string name="nc_sent_poll" formatted="true">%1$s sent a poll.</string>
<string name="nc_sent_location" formatted="true">%1$s sent a location.</string>
<string name="nc_sent_voice" formatted="true">%1$s sent a voice message.</string>
<string name="nc_sent_a_link_you">You sent a link.</string>
@ -317,6 +318,7 @@
<string name="nc_sent_an_audio_you">You sent an audio.</string>
<string name="nc_sent_a_video_you">You sent a video.</string>
<string name="nc_sent_an_image_you">You sent an image.</string>
<string name="nc_sent_poll_you">You sent a poll.</string>
<string name="nc_sent_location_you">You sent a location.</string>
<string name="nc_sent_voice_you">You sent a voice message.</string>
<string name="nc_formatted_message" translatable="false">%1$s: %2$s</string>
@ -435,6 +437,10 @@
<string name="play_pause_voice_message">Play/pause voice message</string>
<string name="nc_voice_message_missing_audio_permission">Permission for audio recording is required</string>
<!-- polls -->
<string name="message_poll_tap_to_vote">Tap to vote</string>
<string name="message_poll_tap_see_results">Tap to see results</string>
<!-- Phonebook Integration -->
<string name="nc_settings_phone_book_integration_key" translatable="false">phone_book_integration</string>
<string name="nc_settings_phone_book_integration_desc">Match contacts based on phone number to integrate Talk shortcut into system contacts app</string>
@ -534,4 +540,5 @@
<string name="call_without_notification">Call without notification</string>
<string name="set_avatar_from_camera">Set avatar from camera</string>
</resources>