mirror of
https://github.com/nextcloud/talk-android
synced 2025-01-19 13:41:26 +00:00
add ability to delete messages
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
a71e3fcd54
commit
ded0cef839
@ -160,7 +160,11 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
|
||||
|
||||
val resources = itemView.resources
|
||||
|
||||
val bg_bubble_color = resources.getColor(R.color.bg_message_list_incoming_bubble)
|
||||
val bg_bubble_color = if (message.isDeleted) {
|
||||
resources.getColor(R.color.bg_message_list_incoming_bubble_deleted)
|
||||
} else {
|
||||
resources.getColor(R.color.bg_message_list_incoming_bubble)
|
||||
}
|
||||
|
||||
var bubbleResource = R.drawable.shape_incoming_message
|
||||
|
||||
@ -229,13 +233,14 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
|
||||
|
||||
// parent message handling
|
||||
|
||||
message.parentMessage?.let { parentChatMessage ->
|
||||
if (!message.isDeleted && message.parentMessage != null) {
|
||||
var parentChatMessage = message.parentMessage
|
||||
parentChatMessage.activeUser = message.activeUser
|
||||
quotedUserAvatar?.load(parentChatMessage.user.avatar) {
|
||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
parentChatMessage.imageUrl?.let{
|
||||
parentChatMessage.imageUrl?.let {
|
||||
quotedMessagePreview?.visibility = View.VISIBLE
|
||||
quotedMessagePreview?.load(it) {
|
||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||
@ -253,7 +258,7 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
|
||||
quotedMessageTime?.setTextColor(context!!.resources.getColor(R.color.warm_grey_four))
|
||||
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)
|
||||
quotedChatMessageView?.visibility = View.VISIBLE
|
||||
} ?: run {
|
||||
} else {
|
||||
quotedChatMessageView?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
@ -136,18 +136,23 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
|
||||
realView.isSelected = true
|
||||
}
|
||||
val resources = sharedApplication!!.resources
|
||||
val bg_bubble_color = 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 = getMessageSelector(
|
||||
resources.getColor(R.color.colorPrimary),
|
||||
bg_bubble_color,
|
||||
resources.getColor(R.color.transparent),
|
||||
resources.getColor(R.color.colorPrimary),
|
||||
bg_bubble_color,
|
||||
R.drawable.shape_grouped_outcoming_message)
|
||||
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||
} else {
|
||||
val bubbleDrawable = getMessageSelector(
|
||||
resources.getColor(R.color.colorPrimary),
|
||||
bg_bubble_color,
|
||||
resources.getColor(R.color.transparent),
|
||||
resources.getColor(R.color.colorPrimary),
|
||||
bg_bubble_color,
|
||||
R.drawable.shape_outcoming_message)
|
||||
ViewCompat.setBackground(bubble, bubbleDrawable)
|
||||
}
|
||||
@ -157,13 +162,14 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
|
||||
|
||||
// parent message handling
|
||||
|
||||
message.parentMessage?.let { parentChatMessage ->
|
||||
if (!message.isDeleted && message.parentMessage != null) {
|
||||
var parentChatMessage = message.parentMessage
|
||||
parentChatMessage.activeUser = message.activeUser
|
||||
quotedUserAvatar?.load(parentChatMessage.user.avatar) {
|
||||
transformations(CircleCropTransformation())
|
||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||
}
|
||||
parentChatMessage.imageUrl?.let{
|
||||
parentChatMessage.imageUrl?.let {
|
||||
quotedMessagePreview?.visibility = View.VISIBLE
|
||||
quotedMessagePreview?.load(it) {
|
||||
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
|
||||
@ -182,7 +188,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
|
||||
quoteColoredView?.setBackgroundResource(R.color.white)
|
||||
|
||||
quotedChatMessageView?.visibility = View.VISIBLE
|
||||
} ?: run {
|
||||
} else {
|
||||
quotedChatMessageView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall;
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverall;
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage;
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall;
|
||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall;
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall;
|
||||
@ -296,7 +297,8 @@ public interface NcApi {
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST
|
||||
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization, @Url String url,
|
||||
Observable<GenericOverall> sendChatMessage(@Header("Authorization") String authorization,
|
||||
@Url String url,
|
||||
@Field("message") CharSequence message,
|
||||
@Field("actorDisplayName") String actorDisplayName,
|
||||
@Field("replyTo") Integer replyTo);
|
||||
@ -355,4 +357,8 @@ public interface NcApi {
|
||||
Observable<Response<GenericOverall>> uploadFile(@Header("Authorization") String authorization,
|
||||
@Url String url,
|
||||
@Body RequestBody body);
|
||||
|
||||
@DELETE
|
||||
Observable<ChatOverallSingleMessage> deleteChatMessage(@Header("Authorization") String authorization,
|
||||
@Url String url);
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverall
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
|
||||
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
@ -114,6 +115,7 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
import org.parceler.Parcels
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Response
|
||||
import java.net.HttpURLConnection
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
@ -1049,7 +1051,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
||||
if (!wasDetached) {
|
||||
if (lookIntoFuture > 0) {
|
||||
val finalTimeout = timeout
|
||||
ncApi?.pullChatMessages(credentials, ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
|
||||
ncApi?.pullChatMessages(credentials,
|
||||
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
|
||||
?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.takeWhile { observable -> inConversation && !wasDetached }
|
||||
@ -1129,7 +1132,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
||||
if (response.code() == 200) {
|
||||
|
||||
val chatOverall = response.body() as ChatOverall?
|
||||
val chatMessageList = chatOverall?.ocs!!.data
|
||||
val chatMessageList = setDeletionFlagsAndRemoveInfomessages(chatOverall?.ocs!!.data)
|
||||
|
||||
if (isFirstMessagesProcessing) {
|
||||
cancelNotificationsForCurrentConversation()
|
||||
@ -1331,6 +1334,31 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
||||
}
|
||||
}
|
||||
|
||||
private fun setDeletionFlagsAndRemoveInfomessages(chatMessageList: List<ChatMessage>): List<ChatMessage> {
|
||||
val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap()
|
||||
val chatMessageIterator = chatMessageMap.iterator()
|
||||
while (chatMessageIterator.hasNext()) {
|
||||
val currentMessage = chatMessageIterator.next()
|
||||
if (isInfoMessageAboutDeletion(currentMessage)) {
|
||||
if (!chatMessageMap.containsKey(currentMessage.value.parentMessage.id)) {
|
||||
// if chatMessageMap doesnt't contain message to delete (this happens when lookingIntoFuture),
|
||||
// the message to delete has to be modified directly inside the adapter
|
||||
setMessageAsDeleted(currentMessage.value.parentMessage)
|
||||
} else {
|
||||
chatMessageMap[currentMessage.value.parentMessage.id]!!.isDeleted = true
|
||||
}
|
||||
chatMessageIterator.remove()
|
||||
}
|
||||
}
|
||||
return chatMessageMap.values.toList()
|
||||
}
|
||||
|
||||
private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry<String, ChatMessage>): Boolean {
|
||||
return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == ChatMessage
|
||||
.SystemMessageType.PARENT_MESSAGE_DELETED
|
||||
}
|
||||
|
||||
|
||||
private fun startACall(isVoiceOnlyCall: Boolean) {
|
||||
isLeavingForConversation = true
|
||||
if (!isVoiceOnlyCall) {
|
||||
@ -1432,15 +1460,82 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.action_delete_message -> {
|
||||
ncApi?.deleteChatMessage(
|
||||
credentials,
|
||||
ApiUtils.getUrlForMessageDeletion(conversationUser?.baseUrl, roomToken, message?.id)
|
||||
)?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<ChatOverallSingleMessage> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
}
|
||||
|
||||
override fun onNext(t: ChatOverallSingleMessage) {
|
||||
if (t.ocs.meta.statusCode == HttpURLConnection.HTTP_ACCEPTED) {
|
||||
Toast.makeText(context, R.string.nc_delete_message_leaked_to_matterbridge,
|
||||
Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Something went wrong when trying to delete message with id " +
|
||||
message?.id, e)
|
||||
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
}
|
||||
})
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
inflate(R.menu.chat_message_menu)
|
||||
menu.findItem(R.id.action_copy_message).isVisible = !(message as ChatMessage).isDeleted
|
||||
menu.findItem(R.id.action_reply_to_message).isVisible = (message as ChatMessage).replyable
|
||||
show()
|
||||
menu.findItem(R.id.action_delete_message).isVisible = isShowMessageDeletionButton(message)
|
||||
if(menu.hasVisibleItems()){
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMessageAsDeleted(message: IMessage?) {
|
||||
val messageTemp = message as ChatMessage
|
||||
messageTemp.isDeleted = true
|
||||
|
||||
messageTemp.isOneToOneConversation = currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
messageTemp.isLinkPreviewAllowed = isLinkPreviewAllowed
|
||||
messageTemp.activeUser = conversationUser
|
||||
|
||||
adapter?.update(messageTemp)
|
||||
}
|
||||
|
||||
private fun isShowMessageDeletionButton(message: ChatMessage): Boolean {
|
||||
if (conversationUser == null) return false
|
||||
|
||||
if(message.systemMessageType != ChatMessage.SystemMessageType.DUMMY) return false
|
||||
|
||||
if(message.isDeleted) return false
|
||||
|
||||
val sixHoursInMillis = 6 * 3600 * 1000
|
||||
val isOlderThanSixHours = message.createdAt?.before(Date(System.currentTimeMillis() - sixHoursInMillis)) == true
|
||||
if(isOlderThanSixHours) return false
|
||||
|
||||
val isUserAllowedByPrivileges = if (message.actorId == conversationUser.userId) {
|
||||
true
|
||||
} else {
|
||||
currentConversation!!.isParticipantOwnerOrModerator
|
||||
}
|
||||
if(!isUserAllowedByPrivileges) return false
|
||||
|
||||
if(!conversationUser.hasSpreedFeatureCapability("delete-messages")) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun hasContentFor(message: IMessage, type: Byte): Boolean {
|
||||
when (type) {
|
||||
CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage)
|
||||
|
@ -21,6 +21,8 @@ package com.nextcloud.talk.models.json.chat;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonIgnore;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
@ -42,7 +44,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import lombok.Data;
|
||||
|
||||
@Parcel
|
||||
@ -59,6 +60,8 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent
|
||||
public Map<String, String> selectedIndividualHashMap;
|
||||
@JsonIgnore
|
||||
public boolean isLinkPreviewAllowed;
|
||||
@JsonIgnore
|
||||
public boolean isDeleted;
|
||||
@JsonField(name = "id")
|
||||
public int jsonMessageId;
|
||||
@JsonField(name = "token")
|
||||
@ -283,6 +286,7 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent
|
||||
FILE_SHARED,
|
||||
LOBBY_NONE,
|
||||
LOBBY_NON_MODERATORS,
|
||||
LOBBY_OPEN_TO_EVERYONE
|
||||
LOBBY_OPEN_TO_EVERYONE,
|
||||
PARENT_MESSAGE_DELETED
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.models.json.chat;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.nextcloud.talk.models.json.generic.GenericOCS;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Parcel
|
||||
@JsonObject
|
||||
public class ChatOCSSingleMessage extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
public ChatMessage data;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.models.json.chat;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Parcel
|
||||
@JsonObject
|
||||
public class ChatOverallSingleMessage {
|
||||
@JsonField(name = "ocs")
|
||||
public ChatOCSSingleMessage ocs;
|
||||
}
|
@ -110,8 +110,12 @@ public class Conversation {
|
||||
}
|
||||
|
||||
public boolean canModerate(UserEntity conversationUser) {
|
||||
return ((Participant.ParticipantType.OWNER.equals(participantType)
|
||||
|| Participant.ParticipantType.MODERATOR.equals(participantType)) && !isLockedOneToOne(conversationUser));
|
||||
return (isParticipantOwnerOrModerator() && !isLockedOneToOne(conversationUser));
|
||||
}
|
||||
|
||||
public boolean isParticipantOwnerOrModerator() {
|
||||
return Participant.ParticipantType.OWNER.equals(participantType)
|
||||
|| Participant.ParticipantType.MODERATOR.equals(participantType);
|
||||
}
|
||||
|
||||
public boolean shouldShowLobby(UserEntity conversationUser) {
|
||||
@ -121,6 +125,7 @@ public class Conversation {
|
||||
public boolean isLobbyViewApplicable(UserEntity conversationUser) {
|
||||
return !canModerate(conversationUser) && (getType() == ConversationType.ROOM_GROUP_CALL || getType() == ConversationType.ROOM_PUBLIC_CALL);
|
||||
}
|
||||
|
||||
public boolean isNameEditable(UserEntity conversationUser) {
|
||||
return (canModerate(conversationUser) && !ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL.equals(type));
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.Syst
|
||||
"lobby_none" -> return LOBBY_NONE
|
||||
"lobby_non_moderators" -> return LOBBY_NON_MODERATORS
|
||||
"lobby_timer_reached" -> return LOBBY_OPEN_TO_EVERYONE
|
||||
"message_deleted" -> return PARENT_MESSAGE_DELETED
|
||||
else -> return DUMMY
|
||||
}
|
||||
}
|
||||
|
@ -292,4 +292,8 @@ public class ApiUtils {
|
||||
public static String getUrlForFileUpload(String baseUrl, String user, String attachmentFolder, String filename) {
|
||||
return baseUrl + "/remote.php/dav/files/" + user + attachmentFolder + "/" + filename;
|
||||
}
|
||||
|
||||
public static String getUrlForMessageDeletion(String baseUrl, String token, String messageId) {
|
||||
return baseUrl + ocsApiVersion + spreedApiVersion + "/chat/" + token + "/" + messageId;
|
||||
}
|
||||
}
|
||||
|
25
app/src/main/res/drawable/ic_delete_white_24dp.xml
Normal file
25
app/src/main/res/drawable/ic_delete_white_24dp.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
@ -6,12 +6,17 @@
|
||||
android:id="@+id/action_copy_message"
|
||||
android:icon="@drawable/ic_content_copy_white_24dp"
|
||||
android:title="@string/nc_copy_message"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_reply_to_message"
|
||||
android:icon="@drawable/ic_reply_white_24dp"
|
||||
android:title="@string/nc_reply"
|
||||
app:showAsAction="always"
|
||||
/>
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete_message"
|
||||
android:icon="@drawable/ic_delete_white_24dp"
|
||||
android:title="@string/nc_delete_message"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
@ -32,6 +32,7 @@
|
||||
<color name="nc_grey">@android:color/holo_purple</color>
|
||||
<color name="bg_bottom_sheet">#222222</color>
|
||||
<color name="bg_message_list_incoming_bubble">#484848</color>
|
||||
<color name="bg_message_list_incoming_bubble_deleted">#66484848</color>
|
||||
|
||||
<color name="textColorMaxContrast">#8c8c8c</color>
|
||||
|
||||
|
@ -59,6 +59,9 @@
|
||||
<color name="bg_dark_mention_chips">#333333</color>
|
||||
|
||||
<color name="bg_message_list_incoming_bubble">#EFEFEF</color>
|
||||
<color name="bg_message_list_incoming_bubble_deleted">#66EFEFEF</color>
|
||||
<color name="bg_message_list_outcoming_bubble">@color/colorPrimary</color>
|
||||
<color name="bg_message_list_outcoming_bubble_deleted">#800082C9</color>
|
||||
|
||||
<color name="bg_bottom_sheet">#46ffffff</color>
|
||||
</resources>
|
||||
|
@ -19,6 +19,9 @@
|
||||
-->
|
||||
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Common -->
|
||||
<string name="nc_common_error_sorry">Sorry, something went wrong!</string>
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
<string name="nc_settings">Settings</string>
|
||||
|
||||
@ -320,6 +323,8 @@
|
||||
<string name="nc_99_plus">99+</string>
|
||||
<string name="nc_copy_message">Copy</string>
|
||||
<string name="nc_reply">Reply</string>
|
||||
<string name="nc_delete_message">Delete</string>
|
||||
<string name="nc_delete_message_leaked_to_matterbridge">Message deleted successfully, but it might have been leaked to other services</string>
|
||||
|
||||
<!-- Upload -->
|
||||
<string name="nc_upload_local_file">Upload local file</string>
|
||||
@ -327,12 +332,13 @@
|
||||
<string name="nc_upload_failed">Failed to upload file</string>
|
||||
<string name="nc_upload_choose_local_files">Choose files</string>
|
||||
|
||||
<!-- Non-translatable strings -->
|
||||
|
||||
<string name="path_password_strike_through" translatable="false"
|
||||
tools:override="true">M3.27,4.27L19.74,20.74</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 in phone book</string>
|
||||
<string name="nc_settings_phone_book_integration_title">Phone book integration</string>
|
||||
<string name="no_phone_book_integration_due_to_permissions">No phone book integration due to missing permissions</string>
|
||||
|
||||
<!-- Non-translatable strings -->
|
||||
<string name="path_password_strike_through" translatable="false"
|
||||
tools:override="true">M3.27,4.27L19.74,20.74</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user