mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-28 20:45:17 +00:00
Merge remote-tracking branch 'upstream/dev' into libsession-integration
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt # app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt
This commit is contained in:
commit
14eb04adcb
@ -40,6 +40,7 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.text.set
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.drawToBitmap
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.Observer
|
||||
@ -409,6 +410,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
updatePlaceholder()
|
||||
setUpBlockedBanner()
|
||||
binding!!.searchBottomBar.setEventListener(this)
|
||||
updateSendAfterApprovalText()
|
||||
showOrHideInputIfNeeded()
|
||||
setUpMessageRequestsBar()
|
||||
|
||||
@ -741,7 +743,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
setUpMessageRequestsBar()
|
||||
invalidateOptionsMenu()
|
||||
updateSubtitle()
|
||||
updateSendAfterApprovalText()
|
||||
showOrHideInputIfNeeded()
|
||||
|
||||
binding?.toolbarContent?.profilePictureView?.root?.update(threadRecipient)
|
||||
binding?.toolbarContent?.conversationTitleView?.text = when {
|
||||
threadRecipient.isLocalNumber -> getString(R.string.note_to_self)
|
||||
@ -750,6 +754,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSendAfterApprovalText() {
|
||||
binding?.textSendAfterApproval?.isVisible = viewModel.showSendAfterApprovalText
|
||||
}
|
||||
|
||||
private fun showOrHideInputIfNeeded() {
|
||||
val recipient = viewModel.recipient
|
||||
if (recipient != null && recipient.isClosedGroupRecipient) {
|
||||
|
@ -203,10 +203,11 @@ public final class ConversationReactionOverlay extends FrameLayout {
|
||||
boolean isMessageOnLeft) {
|
||||
contextMenu = new ConversationContextMenu(dropdownAnchor, getMenuActionItems(messageRecord));
|
||||
|
||||
float itemX = isMessageOnLeft ? scrubberHorizontalMargin :
|
||||
float endX = isMessageOnLeft ? scrubberHorizontalMargin :
|
||||
selectedConversationModel.getBubbleX() - conversationItem.getWidth() + selectedConversationModel.getBubbleWidth();
|
||||
conversationItem.setX(itemX);
|
||||
conversationItem.setY(selectedConversationModel.getBubbleY() - statusBarHeight);
|
||||
float endY = selectedConversationModel.getBubbleY() - statusBarHeight;
|
||||
conversationItem.setX(endX);
|
||||
conversationItem.setY(endY);
|
||||
|
||||
Bitmap conversationItemSnapshot = selectedConversationModel.getBitmap();
|
||||
boolean isWideLayout = contextMenu.getMaxWidth() + scrubberWidth < getWidth();
|
||||
@ -214,8 +215,6 @@ public final class ConversationReactionOverlay extends FrameLayout {
|
||||
int overlayHeight = getHeight();
|
||||
int bubbleWidth = selectedConversationModel.getBubbleWidth();
|
||||
|
||||
float endX = itemX;
|
||||
float endY = conversationItem.getY();
|
||||
float endApparentTop = endY;
|
||||
float endScale = 1f;
|
||||
|
||||
@ -265,9 +264,7 @@ public final class ConversationReactionOverlay extends FrameLayout {
|
||||
}
|
||||
} else {
|
||||
endY = overlayHeight - contextMenu.getMaxHeight() - menuPadding - conversationItemSnapshot.getHeight();
|
||||
|
||||
float contextMenuTop = endY + conversationItemSnapshot.getHeight();
|
||||
reactionBarBackgroundY = getReactionBarOffsetForTouch(selectedConversationModel.getBubbleY(), contextMenuTop, menuPadding, reactionBarOffset, reactionBarHeight, reactionBarTopPadding, endY);
|
||||
reactionBarBackgroundY = endY - reactionBarHeight - menuPadding;
|
||||
}
|
||||
|
||||
endApparentTop = endY;
|
||||
|
@ -35,6 +35,9 @@ class ConversationViewModel(
|
||||
private val storage: Storage
|
||||
) : ViewModel() {
|
||||
|
||||
val showSendAfterApprovalText: Boolean
|
||||
get() = recipient?.run { isContactRecipient && !isLocalNumber && !hasApprovedMe() } ?: false
|
||||
|
||||
private val _uiState = MutableStateFlow(ConversationUiState(conversationExists = true))
|
||||
val uiState: StateFlow<ConversationUiState> = _uiState
|
||||
|
||||
|
@ -126,10 +126,9 @@ open class ThumbnailView: FrameLayout {
|
||||
buildThumbnailGlideRequest(glide, slide).into(GlideDrawableListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, result))
|
||||
}
|
||||
slide.hasPlaceholder() -> {
|
||||
buildPlaceholderGlideRequest(glide, slide).into(GlideBitmapListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, result))
|
||||
buildPlaceholderGlideRequest(glide, slide).into(GlideBitmapListeningTarget(binding.thumbnailImage, null, result))
|
||||
}
|
||||
else -> {
|
||||
binding.thumbnailLoadIndicator.isVisible = false
|
||||
glide.clear(binding.thumbnailImage)
|
||||
result.set(false)
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import org.thoughtcrime.securesms.database.model.Quote
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent.Companion.get
|
||||
import org.thoughtcrime.securesms.mms.MmsException
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck
|
||||
import org.thoughtcrime.securesms.util.asSequence
|
||||
import java.io.Closeable
|
||||
import java.io.IOException
|
||||
import java.security.SecureRandom
|
||||
@ -86,54 +87,22 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
return 0
|
||||
}
|
||||
|
||||
fun addFailures(messageId: Long, failure: List<NetworkFailure>) {
|
||||
try {
|
||||
addToDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList::class.java)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, e)
|
||||
fun isOutgoingMessage(timestamp: Long): Boolean =
|
||||
databaseHelper.writableDatabase.query(
|
||||
TABLE_NAME,
|
||||
arrayOf(ID, THREAD_ID, MESSAGE_BOX, ADDRESS),
|
||||
DATE_SENT + " = ?",
|
||||
arrayOf(timestamp.toString()),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).use { cursor ->
|
||||
cursor.asSequence()
|
||||
.map { cursor.getColumnIndexOrThrow(MESSAGE_BOX) }
|
||||
.map(cursor::getLong)
|
||||
.any { MmsSmsColumns.Types.isOutgoingMessageType(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun removeFailure(messageId: Long, failure: NetworkFailure?) {
|
||||
try {
|
||||
removeFromDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList::class.java)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
|
||||
fun isOutgoingMessage(timestamp: Long): Boolean {
|
||||
val database = databaseHelper.writableDatabase
|
||||
var cursor: Cursor? = null
|
||||
var isOutgoing = false
|
||||
try {
|
||||
cursor = database.query(
|
||||
TABLE_NAME,
|
||||
arrayOf<String>(ID, THREAD_ID, MESSAGE_BOX, ADDRESS),
|
||||
DATE_SENT + " = ?",
|
||||
arrayOf(timestamp.toString()),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
while (cursor.moveToNext()) {
|
||||
if (MmsSmsColumns.Types.isOutgoingMessageType(
|
||||
cursor.getLong(
|
||||
cursor.getColumnIndexOrThrow(
|
||||
MESSAGE_BOX
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
isOutgoing = true
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return isOutgoing
|
||||
}
|
||||
|
||||
fun incrementReceiptCount(
|
||||
messageId: SyncMessageId,
|
||||
@ -230,6 +199,25 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(RecipientFormattingException::class, MmsException::class)
|
||||
private fun getThreadIdFor(retrieved: IncomingMediaMessage): Long {
|
||||
return if (retrieved.groupId != null) {
|
||||
val groupRecipients = Recipient.from(
|
||||
context,
|
||||
retrieved.groupId,
|
||||
true
|
||||
)
|
||||
get(context).threadDatabase().getOrCreateThreadIdFor(groupRecipients)
|
||||
} else {
|
||||
val sender = Recipient.from(
|
||||
context,
|
||||
retrieved.from,
|
||||
true
|
||||
)
|
||||
get(context).threadDatabase().getOrCreateThreadIdFor(sender)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rawQuery(where: String, arguments: Array<String>?): Cursor {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.rawQuery(
|
||||
@ -269,48 +257,30 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
}
|
||||
}
|
||||
|
||||
fun markAsPendingInsecureSmsFallback(messageId: Long) {
|
||||
val threadId = getThreadIdForMessage(messageId)
|
||||
private fun markAs(
|
||||
messageId: Long,
|
||||
baseType: Long,
|
||||
threadId: Long = getThreadIdForMessage(messageId)
|
||||
) {
|
||||
updateMailboxBitmask(
|
||||
messageId,
|
||||
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
||||
MmsSmsColumns.Types.BASE_PENDING_INSECURE_SMS_FALLBACK,
|
||||
baseType,
|
||||
Optional.of(threadId)
|
||||
)
|
||||
notifyConversationListeners(threadId)
|
||||
}
|
||||
|
||||
fun markAsSending(messageId: Long) {
|
||||
val threadId = getThreadIdForMessage(messageId)
|
||||
updateMailboxBitmask(
|
||||
messageId,
|
||||
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
||||
MmsSmsColumns.Types.BASE_SENDING_TYPE,
|
||||
Optional.of(threadId)
|
||||
)
|
||||
notifyConversationListeners(threadId)
|
||||
markAs(messageId, MmsSmsColumns.Types.BASE_SENDING_TYPE)
|
||||
}
|
||||
|
||||
fun markAsSentFailed(messageId: Long) {
|
||||
val threadId = getThreadIdForMessage(messageId)
|
||||
updateMailboxBitmask(
|
||||
messageId,
|
||||
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
||||
MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE,
|
||||
Optional.of(threadId)
|
||||
)
|
||||
notifyConversationListeners(threadId)
|
||||
markAs(messageId, MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE)
|
||||
}
|
||||
|
||||
override fun markAsSent(messageId: Long, secure: Boolean) {
|
||||
val threadId = getThreadIdForMessage(messageId)
|
||||
updateMailboxBitmask(
|
||||
messageId,
|
||||
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
||||
MmsSmsColumns.Types.BASE_SENT_TYPE or if (secure) MmsSmsColumns.Types.PUSH_MESSAGE_BIT or MmsSmsColumns.Types.SECURE_MESSAGE_BIT else 0,
|
||||
Optional.of(threadId)
|
||||
)
|
||||
notifyConversationListeners(threadId)
|
||||
markAs(messageId, MmsSmsColumns.Types.BASE_SENT_TYPE or if (secure) MmsSmsColumns.Types.PUSH_MESSAGE_BIT or MmsSmsColumns.Types.SECURE_MESSAGE_BIT else 0)
|
||||
}
|
||||
|
||||
override fun markUnidentified(messageId: Long, unidentified: Boolean) {
|
||||
@ -331,13 +301,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
queue(Runnable { attachmentDatabase.deleteAttachmentsForMessage(messageId) })
|
||||
val threadId = getThreadIdForMessage(messageId)
|
||||
|
||||
updateMailboxBitmask(
|
||||
messageId,
|
||||
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
||||
MmsSmsColumns.Types.BASE_DELETED_TYPE,
|
||||
Optional.of(threadId)
|
||||
)
|
||||
notifyConversationListeners(threadId)
|
||||
markAs(messageId, MmsSmsColumns.Types.BASE_DELETED_TYPE, threadId)
|
||||
}
|
||||
|
||||
override fun markExpireStarted(messageId: Long) {
|
||||
@ -374,10 +338,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
)
|
||||
}
|
||||
|
||||
fun setAllMessagesRead(): List<MarkedMessageInfo> {
|
||||
return setMessagesRead(READ + " = 0", null)
|
||||
}
|
||||
|
||||
private fun setMessagesRead(where: String, arguments: Array<String>?): List<MarkedMessageInfo> {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val result: MutableList<MarkedMessageInfo> = LinkedList()
|
||||
@ -386,7 +346,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
try {
|
||||
cursor = database.query(
|
||||
TABLE_NAME,
|
||||
arrayOf<String>(ID, ADDRESS, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED),
|
||||
arrayOf(ID, ADDRESS, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED),
|
||||
where,
|
||||
arguments,
|
||||
null,
|
||||
@ -1333,25 +1293,16 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
val attachments = get(context).attachmentDatabase().getAttachment(
|
||||
cursor
|
||||
)
|
||||
val contacts: List<Contact?> = getSharedContacts(
|
||||
cursor, attachments
|
||||
)
|
||||
val contactAttachments =
|
||||
contacts.map { obj: Contact? -> obj!!.avatarAttachment }
|
||||
.filter { a: Attachment? -> a != null }
|
||||
.toSet()
|
||||
val previews: List<LinkPreview?> = getLinkPreviews(
|
||||
cursor, attachments
|
||||
)
|
||||
val previewAttachments =
|
||||
previews.filter { lp: LinkPreview? -> lp!!.getThumbnail().isPresent }
|
||||
.map { lp: LinkPreview? -> lp!!.getThumbnail().get() }
|
||||
.toSet()
|
||||
val contacts: List<Contact?> = getSharedContacts(cursor, attachments)
|
||||
val contactAttachments: Set<Attachment?> =
|
||||
contacts.mapNotNull { it?.avatarAttachment }.toSet()
|
||||
val previews: List<LinkPreview?> = getLinkPreviews(cursor, attachments)
|
||||
val previewAttachments: Set<Attachment?> =
|
||||
previews.mapNotNull { it?.getThumbnail()?.orNull() }.toSet()
|
||||
val slideDeck = getSlideDeck(
|
||||
Stream.of(attachments)
|
||||
.filterNot { o: DatabaseAttachment? -> contactAttachments.contains(o) }
|
||||
.filterNot { o: DatabaseAttachment? -> previewAttachments.contains(o) }
|
||||
.toList()
|
||||
attachments
|
||||
.filterNot { o: DatabaseAttachment? -> o in contactAttachments }
|
||||
.filterNot { o: DatabaseAttachment? -> o in previewAttachments }
|
||||
)
|
||||
val quote = getQuote(cursor)
|
||||
val reactions = get(context).reactionDatabase().getReactions(cursor)
|
||||
|
@ -0,0 +1,6 @@
|
||||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import android.database.Cursor
|
||||
|
||||
fun Cursor.asSequence(): Sequence<Cursor> =
|
||||
generateSequence { if (moveToNext()) this else null }
|
@ -35,7 +35,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="36dp"
|
||||
android:visibility="gone"
|
||||
android:layout_above="@+id/messageRequestBar"
|
||||
android:layout_above="@+id/textSendAfterApproval"
|
||||
/>
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar
|
||||
@ -118,6 +118,19 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textSendAfterApproval"
|
||||
android:text="@string/ConversationActivity_send_after_approval"
|
||||
android:visibility="gone"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/classic_light_2"
|
||||
android:padding="22dp"
|
||||
android:textSize="12sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:layout_above="@id/messageRequestBar"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/scrollToBottomButton"
|
||||
android:visibility="gone"
|
||||
|
@ -3,9 +3,9 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:contentDescription="@string/AccessibilityId_message_request_banner"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/conversation_view_background"
|
||||
android:contentDescription="@string/AccessibilityId_message_request_banner"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/accent_line_thickness"
|
||||
@ -72,11 +72,15 @@
|
||||
android:alpha="0.4"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAlignment="textEnd"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toEndOf="@id/unreadCountIndicator"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="9:41 AM" />
|
||||
tools:text="11 Apr, 9:41 AM" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -227,6 +227,7 @@
|
||||
<string name="ConversationActivity_search_position">%1$d of %2$d</string>
|
||||
<string name="ConversationActivity_call_title">Call Permissions Required</string>
|
||||
<string name="ConversationActivity_call_prompt">You can enable the \'Voice and video calls\' permission in the Privacy Settings.</string>
|
||||
<string name="ConversationActivity_send_after_approval">You will be able to send voice messages and attachments once the recipient has approved this message request</string>
|
||||
<!-- ConversationFragment -->
|
||||
<plurals name="ConversationFragment_delete_selected_messages">
|
||||
<item quantity="one">Delete selected message?</item>
|
||||
|
Loading…
Reference in New Issue
Block a user