mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
add data classes for polls (WIP)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
12efda5064
commit
f2025332ab
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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) {
|
||||
@ -152,7 +154,7 @@ public class ApiUtils {
|
||||
return version;
|
||||
}
|
||||
if (version == APIv1 &&
|
||||
user.hasSpreedFeatureCapability("mention-flag") &&
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
10
app/src/main/res/drawable/ic_baseline_bar_chart_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_bar_chart_24.xml
Normal 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>
|
109
app/src/main/res/layout/item_custom_incoming_poll_message.xml
Normal file
109
app/src/main/res/layout/item_custom_incoming_poll_message.xml
Normal 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>
|
@ -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>
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user