mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 16:57:50 +00:00
Merge branch 'dev' into ses-1733
This commit is contained in:
commit
c84f543069
@ -68,7 +68,7 @@ enum class ExpiryType(
|
|||||||
AFTER_SEND(
|
AFTER_SEND(
|
||||||
ExpiryMode::AfterSend,
|
ExpiryMode::AfterSend,
|
||||||
R.string.expiration_type_disappear_after_send,
|
R.string.expiration_type_disappear_after_send,
|
||||||
R.string.expiration_type_disappear_after_read_description,
|
R.string.expiration_type_disappear_after_send_description,
|
||||||
R.string.AccessibilityId_disappear_after_send_option
|
R.string.AccessibilityId_disappear_after_send_option
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1251,6 +1251,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
|
|
||||||
// `position` is the adapter position; not the visual position
|
// `position` is the adapter position; not the visual position
|
||||||
private fun handleSwipeToReply(message: MessageRecord) {
|
private fun handleSwipeToReply(message: MessageRecord) {
|
||||||
|
if (message.isOpenGroupInvitation) return
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
binding?.inputBar?.draftQuote(recipient, message, glide)
|
binding?.inputBar?.draftQuote(recipient, message, glide)
|
||||||
}
|
}
|
||||||
|
@ -532,7 +532,7 @@ class ConversationReactionOverlay : FrameLayout {
|
|||||||
items += ActionItem(R.attr.menu_select_icon, R.string.conversation_context__menu_select, { handleActionItemClicked(Action.SELECT) }, R.string.AccessibilityId_select)
|
items += ActionItem(R.attr.menu_select_icon, R.string.conversation_context__menu_select, { handleActionItemClicked(Action.SELECT) }, R.string.AccessibilityId_select)
|
||||||
// Reply
|
// Reply
|
||||||
val canWrite = openGroup == null || openGroup.canWrite
|
val canWrite = openGroup == null || openGroup.canWrite
|
||||||
if (canWrite && !message.isPending && !message.isFailed) {
|
if (canWrite && !message.isPending && !message.isFailed && !message.isOpenGroupInvitation) {
|
||||||
items += ActionItem(R.attr.menu_reply_icon, R.string.conversation_context__menu_reply, { handleActionItemClicked(Action.REPLY) }, R.string.AccessibilityId_reply_message)
|
items += ActionItem(R.attr.menu_reply_icon, R.string.conversation_context__menu_reply, { handleActionItemClicked(Action.REPLY) }, R.string.AccessibilityId_reply_message)
|
||||||
}
|
}
|
||||||
// Copy message text
|
// Copy message text
|
||||||
|
@ -123,7 +123,7 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
AppTheme {
|
AppTheme {
|
||||||
MessageDetails(
|
MessageDetails(
|
||||||
state = state,
|
state = state,
|
||||||
onReply = { setResultAndFinish(ON_REPLY) },
|
onReply = if (state.canReply) { { setResultAndFinish(ON_REPLY) } } else null,
|
||||||
onResend = state.error?.let { { setResultAndFinish(ON_RESEND) } },
|
onResend = state.error?.let { { setResultAndFinish(ON_RESEND) } },
|
||||||
onDelete = { setResultAndFinish(ON_DELETE) },
|
onDelete = { setResultAndFinish(ON_DELETE) },
|
||||||
onClickImage = { viewModel.onClickImage(it) },
|
onClickImage = { viewModel.onClickImage(it) },
|
||||||
@ -145,7 +145,7 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun MessageDetails(
|
fun MessageDetails(
|
||||||
state: MessageDetailsState,
|
state: MessageDetailsState,
|
||||||
onReply: () -> Unit = {},
|
onReply: (() -> Unit)? = null,
|
||||||
onResend: (() -> Unit)? = null,
|
onResend: (() -> Unit)? = null,
|
||||||
onDelete: () -> Unit = {},
|
onDelete: () -> Unit = {},
|
||||||
onClickImage: (Int) -> Unit = {},
|
onClickImage: (Int) -> Unit = {},
|
||||||
@ -214,18 +214,20 @@ fun CellMetadata(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CellButtons(
|
fun CellButtons(
|
||||||
onReply: () -> Unit = {},
|
onReply: (() -> Unit)? = null,
|
||||||
onResend: (() -> Unit)? = null,
|
onResend: (() -> Unit)? = null,
|
||||||
onDelete: () -> Unit = {},
|
onDelete: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Cell {
|
Cell {
|
||||||
Column {
|
Column {
|
||||||
ItemButton(
|
onReply?.let {
|
||||||
stringResource(R.string.reply),
|
ItemButton(
|
||||||
R.drawable.ic_message_details__reply,
|
stringResource(R.string.reply),
|
||||||
onClick = onReply
|
R.drawable.ic_message_details__reply,
|
||||||
)
|
onClick = it
|
||||||
Divider()
|
)
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
onResend?.let {
|
onResend?.let {
|
||||||
ItemButton(
|
ItemButton(
|
||||||
stringResource(R.string.resend),
|
stringResource(R.string.resend),
|
||||||
|
@ -117,7 +117,7 @@ class MessageDetailsViewModel @Inject constructor(
|
|||||||
Attachment(slide.details, slide.fileName.orNull(), slide.uri, slide is ImageSlide)
|
Attachment(slide.details, slide.fileName.orNull(), slide.uri, slide is ImageSlide)
|
||||||
|
|
||||||
fun onClickImage(index: Int) {
|
fun onClickImage(index: Int) {
|
||||||
val state = state.value ?: return
|
val state = state.value
|
||||||
val mmsRecord = state.mmsRecord ?: return
|
val mmsRecord = state.mmsRecord ?: return
|
||||||
val slide = mmsRecord.slideDeck.slides[index] ?: return
|
val slide = mmsRecord.slideDeck.slides[index] ?: return
|
||||||
// only open to downloaded images
|
// only open to downloaded images
|
||||||
@ -158,6 +158,7 @@ data class MessageDetailsState(
|
|||||||
val thread: Recipient? = null,
|
val thread: Recipient? = null,
|
||||||
) {
|
) {
|
||||||
val fromTitle = GetString(R.string.message_details_header__from)
|
val fromTitle = GetString(R.string.message_details_header__from)
|
||||||
|
val canReply = record?.isOpenGroupInvitation != true
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Attachment(
|
data class Attachment(
|
||||||
|
@ -77,7 +77,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
|
|||||||
&& firstMessage.isMms && (firstMessage as MediaMmsMessageRecord).containsMediaSlide())
|
&& firstMessage.isMms && (firstMessage as MediaMmsMessageRecord).containsMediaSlide())
|
||||||
// Reply
|
// Reply
|
||||||
menu.findItem(R.id.menu_context_reply).isVisible =
|
menu.findItem(R.id.menu_context_reply).isVisible =
|
||||||
(selectedItems.size == 1 && !firstMessage.isPending && !firstMessage.isFailed)
|
(selectedItems.size == 1 && !firstMessage.isPending && !firstMessage.isFailed && !firstMessage.isOpenGroupInvitation)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu): Boolean {
|
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu): Boolean {
|
||||||
|
@ -37,6 +37,7 @@ import org.session.libsession.utilities.ViewUtil
|
|||||||
import org.session.libsession.utilities.getColorFromAttr
|
import org.session.libsession.utilities.getColorFromAttr
|
||||||
import org.session.libsession.utilities.modifyLayoutParams
|
import org.session.libsession.utilities.modifyLayoutParams
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
||||||
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
||||||
@ -65,6 +66,7 @@ private const val TAG = "VisibleMessageView"
|
|||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class VisibleMessageView : LinearLayout {
|
class VisibleMessageView : LinearLayout {
|
||||||
|
private var replyDisabled: Boolean = false
|
||||||
@Inject lateinit var threadDb: ThreadDatabase
|
@Inject lateinit var threadDb: ThreadDatabase
|
||||||
@Inject lateinit var lokiThreadDb: LokiThreadDatabase
|
@Inject lateinit var lokiThreadDb: LokiThreadDatabase
|
||||||
@Inject lateinit var lokiApiDb: LokiAPIDatabase
|
@Inject lateinit var lokiApiDb: LokiAPIDatabase
|
||||||
@ -135,6 +137,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
onAttachmentNeedsDownload: (Long, Long) -> Unit,
|
onAttachmentNeedsDownload: (Long, Long) -> Unit,
|
||||||
lastSentMessageId: Long
|
lastSentMessageId: Long
|
||||||
) {
|
) {
|
||||||
|
replyDisabled = message.isOpenGroupInvitation
|
||||||
val threadID = message.threadId
|
val threadID = message.threadId
|
||||||
val thread = threadDb.getRecipientForThreadId(threadID) ?: return
|
val thread = threadDb.getRecipientForThreadId(threadID) ?: return
|
||||||
val isGroupThread = thread.isGroupRecipient
|
val isGroupThread = thread.isGroupRecipient
|
||||||
@ -206,7 +209,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
binding.dateBreakTextView.text = if (showDateBreak) DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp) else null
|
binding.dateBreakTextView.text = if (showDateBreak) DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp) else null
|
||||||
binding.dateBreakTextView.isVisible = showDateBreak
|
binding.dateBreakTextView.isVisible = showDateBreak
|
||||||
|
|
||||||
// Message status indicator
|
// Update message status indicator
|
||||||
showStatusMessage(message)
|
showStatusMessage(message)
|
||||||
|
|
||||||
// Emoji Reactions
|
// Emoji Reactions
|
||||||
@ -243,44 +246,101 @@ class VisibleMessageView : LinearLayout {
|
|||||||
onDoubleTap = { binding.messageContentView.root.onContentDoubleTap?.invoke() }
|
onDoubleTap = { binding.messageContentView.root.onContentDoubleTap?.invoke() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method to display or hide the status of a message.
|
||||||
|
// Note: Although most commonly used to display the delivery status of a message, we also use the
|
||||||
|
// message status area to display the disappearing messages state - so in this latter case we'll
|
||||||
|
// be displaying the "Sent" and the animating clock icon for outgoing messages or "Read" and the
|
||||||
|
// animated clock icon for incoming messages.
|
||||||
private fun showStatusMessage(message: MessageRecord) {
|
private fun showStatusMessage(message: MessageRecord) {
|
||||||
|
// We'll start by hiding everything and then only make visible what we need
|
||||||
|
binding.messageStatusTextView.isVisible = false
|
||||||
|
binding.messageStatusImageView.isVisible = false
|
||||||
|
binding.expirationTimerView.isVisible = false
|
||||||
|
|
||||||
val scheduledToDisappear = message.expiresIn > 0
|
// Get details regarding how we should display the message (it's delivery icon, icon tint colour, and
|
||||||
|
// the resource string for what text to display (R.string.delivery_status_sent etc.).
|
||||||
|
val (iconID, iconColor, textId) = getMessageStatusInfo(message)
|
||||||
|
|
||||||
|
// If we get any nulls then a message isn't one with a state that we care about (i.e., control messages
|
||||||
|
// etc.) - so bail. See: `DisplayRecord.is<WHATEVER>` for the full suite of message state methods.
|
||||||
|
// Also: We set all delivery status elements visibility to false just to make sure we don't display any
|
||||||
|
// stale data.
|
||||||
|
if (textId == null) return
|
||||||
|
|
||||||
binding.messageInnerLayout.modifyLayoutParams<FrameLayout.LayoutParams> {
|
binding.messageInnerLayout.modifyLayoutParams<FrameLayout.LayoutParams> {
|
||||||
gravity = if (message.isOutgoing) Gravity.END else Gravity.START
|
gravity = if (message.isOutgoing) Gravity.END else Gravity.START
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.statusContainer.modifyLayoutParams<ConstraintLayout.LayoutParams> {
|
binding.statusContainer.modifyLayoutParams<ConstraintLayout.LayoutParams> {
|
||||||
horizontalBias = if (message.isOutgoing) 1f else 0f
|
horizontalBias = if (message.isOutgoing) 1f else 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.expirationTimerView.isGone = true
|
// If the message is incoming AND it is not scheduled to disappear then don't show any status or timer details
|
||||||
|
val scheduledToDisappear = message.expiresIn > 0
|
||||||
|
if (message.isIncoming && !scheduledToDisappear) return
|
||||||
|
|
||||||
if (message.isOutgoing || scheduledToDisappear) {
|
// Set text & icons as appropriate for the message state. Note: Possible message states we care
|
||||||
val (iconID, iconColor, textId) = getMessageStatusImage(message)
|
// about are: isFailed, isSyncFailed, isPending, isSyncing, isResyncing, isRead, and isSent.
|
||||||
textId?.let(binding.messageStatusTextView::setText)
|
textId.let(binding.messageStatusTextView::setText)
|
||||||
iconColor?.let(binding.messageStatusTextView::setTextColor)
|
iconColor?.let(binding.messageStatusTextView::setTextColor)
|
||||||
iconID?.let { ContextCompat.getDrawable(context, it) }
|
iconID?.let { ContextCompat.getDrawable(context, it) }
|
||||||
?.run { iconColor?.let { mutate().apply { setTint(it) } } ?: this }
|
?.run { iconColor?.let { mutate().apply { setTint(it) } } ?: this }
|
||||||
?.let(binding.messageStatusImageView::setImageDrawable)
|
?.let(binding.messageStatusImageView::setImageDrawable)
|
||||||
|
|
||||||
// Always show the delivery status of the last sent message
|
// Potential options at this point are that the message is:
|
||||||
val thisUsersSessionId = TextSecurePreferences.getLocalNumber(context)
|
// i.) incoming AND scheduled to disappear.
|
||||||
val lastSentMessageId = mmsSmsDb.getLastSentMessageFromSender(message.threadId, thisUsersSessionId)
|
// ii.) outgoing but NOT scheduled to disappear, or
|
||||||
val isLastSentMessage = lastSentMessageId == message.id
|
// iii.) outgoing AND scheduled to disappear.
|
||||||
|
|
||||||
binding.messageStatusTextView.isVisible = textId != null && (isLastSentMessage || scheduledToDisappear)
|
// ----- Case i..) Message is incoming and scheduled to disappear -----
|
||||||
val showTimer = scheduledToDisappear && !message.isPending
|
if (message.isIncoming && scheduledToDisappear) {
|
||||||
binding.messageStatusImageView.isVisible = iconID != null && !showTimer && (!message.isSent || isLastSentMessage)
|
// Display the status ('Read') and the show the timer only (no delivery icon)
|
||||||
|
binding.messageStatusTextView.isVisible = true
|
||||||
binding.messageStatusImageView.bringToFront()
|
binding.expirationTimerView.isVisible = true
|
||||||
binding.expirationTimerView.bringToFront()
|
binding.expirationTimerView.bringToFront()
|
||||||
binding.expirationTimerView.isVisible = showTimer
|
updateExpirationTimer(message)
|
||||||
if (showTimer) updateExpirationTimer(message)
|
return
|
||||||
} else {
|
}
|
||||||
binding.messageStatusTextView.isVisible = false
|
|
||||||
binding.messageStatusImageView.isVisible = false
|
// --- If we got here then we know the message is outgoing ---
|
||||||
|
|
||||||
|
val thisUsersSessionId = TextSecurePreferences.getLocalNumber(context)
|
||||||
|
val lastSentMessageId = mmsSmsDb.getLastSentMessageFromSender(message.threadId, thisUsersSessionId)
|
||||||
|
val isLastSentMessage = lastSentMessageId == message.id
|
||||||
|
|
||||||
|
// ----- Case ii.) Message is outgoing but NOT scheduled to disappear -----
|
||||||
|
if (!scheduledToDisappear) {
|
||||||
|
// If this isn't a disappearing message then we never show the timer
|
||||||
|
|
||||||
|
// If the message has NOT been successfully sent then always show the delivery status text and icon..
|
||||||
|
val neitherSentNorRead = !(message.isSent || message.isRead)
|
||||||
|
if (neitherSentNorRead) {
|
||||||
|
binding.messageStatusTextView.isVisible = true
|
||||||
|
binding.messageStatusImageView.isVisible = true
|
||||||
|
} else {
|
||||||
|
// ..but if the message HAS been successfully sent or read then only display the delivery status
|
||||||
|
// text and image if this is the last sent message.
|
||||||
|
binding.messageStatusTextView.isVisible = isLastSentMessage
|
||||||
|
binding.messageStatusImageView.isVisible = isLastSentMessage
|
||||||
|
if (isLastSentMessage) { binding.messageStatusImageView.bringToFront() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // ----- Case iii.) Message is outgoing AND scheduled to disappear -----
|
||||||
|
{
|
||||||
|
// Always display the delivery status text on all outgoing disappearing messages
|
||||||
|
binding.messageStatusTextView.isVisible = true
|
||||||
|
|
||||||
|
// If the message is sent or has been read..
|
||||||
|
val sentOrRead = message.isSent || message.isRead
|
||||||
|
if (sentOrRead) {
|
||||||
|
// ..then display the timer icon for this disappearing message (but keep the message status icon hidden)
|
||||||
|
binding.expirationTimerView.isVisible = true
|
||||||
|
binding.expirationTimerView.bringToFront()
|
||||||
|
updateExpirationTimer(message)
|
||||||
|
} else {
|
||||||
|
// If the message has NOT been sent or read (or it has failed) then show the delivery status icon rather than the timer icon
|
||||||
|
binding.messageStatusImageView.isVisible = true
|
||||||
|
binding.messageStatusImageView.bringToFront()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,10 +362,9 @@ class VisibleMessageView : LinearLayout {
|
|||||||
@ColorInt val iconTint: Int?,
|
@ColorInt val iconTint: Int?,
|
||||||
@StringRes val messageText: Int?)
|
@StringRes val messageText: Int?)
|
||||||
|
|
||||||
private fun getMessageStatusImage(message: MessageRecord): MessageStatusInfo = when {
|
private fun getMessageStatusInfo(message: MessageRecord): MessageStatusInfo = when {
|
||||||
message.isFailed ->
|
message.isFailed ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(R.drawable.ic_delivery_status_failed,
|
||||||
R.drawable.ic_delivery_status_failed,
|
|
||||||
resources.getColor(R.color.destructive, context.theme),
|
resources.getColor(R.color.destructive, context.theme),
|
||||||
R.string.delivery_status_failed
|
R.string.delivery_status_failed
|
||||||
)
|
)
|
||||||
@ -318,24 +377,32 @@ class VisibleMessageView : LinearLayout {
|
|||||||
message.isPending ->
|
message.isPending ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_sending,
|
R.drawable.ic_delivery_status_sending,
|
||||||
context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_sending
|
context.getColorFromAttr(R.attr.message_status_color),
|
||||||
|
R.string.delivery_status_sending
|
||||||
)
|
)
|
||||||
message.isResyncing ->
|
message.isSyncing || message.isResyncing ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_sending,
|
R.drawable.ic_delivery_status_sending,
|
||||||
context.getColor(R.color.accent_orange), R.string.delivery_status_syncing
|
context.getColorFromAttr(R.attr.message_status_color),
|
||||||
|
R.string.delivery_status_sending // We COULD tell the user that we're `syncing` (R.string.delivery_status_syncing) but it will likely make more sense to them if we say "Sending"
|
||||||
)
|
)
|
||||||
message.isRead || !message.isOutgoing ->
|
message.isRead || message.isIncoming ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_read,
|
R.drawable.ic_delivery_status_read,
|
||||||
context.getColorFromAttr(R.attr.message_status_color), R.string.delivery_status_read
|
context.getColorFromAttr(R.attr.message_status_color),
|
||||||
|
R.string.delivery_status_read
|
||||||
)
|
)
|
||||||
else ->
|
message.isSent ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_sent,
|
R.drawable.ic_delivery_status_sent,
|
||||||
context.getColorFromAttr(R.attr.message_status_color),
|
context.getColorFromAttr(R.attr.message_status_color),
|
||||||
R.string.delivery_status_sent
|
R.string.delivery_status_sent
|
||||||
)
|
)
|
||||||
|
else -> {
|
||||||
|
// The message isn't one we care about for message statuses we display to the user (i.e.,
|
||||||
|
// control messages etc. - see the `DisplayRecord.is<WHATEVER>` suite of methods for options).
|
||||||
|
MessageStatusInfo(null, null, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateExpirationTimer(message: MessageRecord) {
|
private fun updateExpirationTimer(message: MessageRecord) {
|
||||||
@ -409,6 +476,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
} else {
|
} else {
|
||||||
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
|
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
|
||||||
}
|
}
|
||||||
|
if (replyDisabled) return
|
||||||
if (translationX > 0) { return } // Only allow swipes to the left
|
if (translationX > 0) { return } // Only allow swipes to the left
|
||||||
// The idea here is to asymptotically approach a maximum drag distance
|
// The idea here is to asymptotically approach a maximum drag distance
|
||||||
val damping = 50.0f
|
val damping = 50.0f
|
||||||
|
@ -114,6 +114,11 @@ public abstract class DisplayRecord {
|
|||||||
public boolean isOutgoing() {
|
public boolean isOutgoing() {
|
||||||
return MmsSmsColumns.Types.isOutgoingMessageType(type);
|
return MmsSmsColumns.Types.isOutgoingMessageType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isIncoming() {
|
||||||
|
return !MmsSmsColumns.Types.isOutgoingMessageType(type);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isGroupUpdateMessage() {
|
public boolean isGroupUpdateMessage() {
|
||||||
return SmsDatabase.Types.isGroupUpdateMessage(type);
|
return SmsDatabase.Types.isGroupUpdateMessage(type);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user