diff --git a/app/build.gradle b/app/build.gradle
index 26a37e8200..cf772c0a75 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -143,7 +143,7 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.2'
}
-def canonicalVersionCode = 184
+def canonicalVersionCode = 186
def canonicalVersionName = "1.11.0"
def postFixSize = 10
diff --git a/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java
index a4a17b7389..8f820cba54 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/ShareActivity.java
@@ -39,6 +39,7 @@ import org.session.libsession.utilities.DistributionTypes;
import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.session.libsession.utilities.Address;
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
@@ -215,9 +216,9 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
}
private void createConversation(long threadId, Address address, int distributionType) {
- final Intent intent = getBaseShareIntent(ConversationActivity.class);
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, address);
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
+ final Intent intent = getBaseShareIntent(ConversationActivityV2.class);
+ intent.putExtra(ConversationActivityV2.ADDRESS, address);
+ intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
isPassingAlongMedia = true;
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java
index ac5ee5004c..b93ae4ab65 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java
@@ -14,6 +14,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.ApplicationContext;
+import org.thoughtcrime.securesms.conversation.v2.components.ExpirationTimerView;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
index f288ac5ba5..ece1bb784d 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
@@ -14,6 +14,7 @@ import android.view.WindowManager;
import org.session.libsignal.utilities.Log;
import org.session.libsignal.utilities.ListenableFuture;
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import java.util.concurrent.ExecutionException;
@@ -80,9 +81,9 @@ public class ConversationPopupActivity extends ConversationActivity {
@Override
public void onSuccess(Long result) {
ActivityOptionsCompat transition = ActivityOptionsCompat.makeScaleUpAnimation(getWindow().getDecorView(), 0, 0, getWindow().getAttributes().width, getWindow().getAttributes().height);
- Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivity.class);
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, getRecipient().getAddress());
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, result);
+ Intent intent = new Intent(ConversationPopupActivity.this, ConversationActivityV2.class);
+ intent.putExtra(ConversationActivityV2.ADDRESS, getRecipient().getAddress());
+ intent.putExtra(ConversationActivityV2.THREAD_ID, result);
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
startActivity(intent, transition.toBundle());
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
index 3065ca13cc..e2abbff332 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
@@ -18,10 +18,14 @@ import android.util.Log
import android.util.Pair
import android.util.TypedValue
import android.view.*
+import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.Toast
+import androidx.annotation.DimenRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
@@ -57,16 +61,21 @@ import org.session.libsession.messaging.sending_receiving.link_preview.LinkPrevi
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
import org.session.libsession.messaging.utilities.UpdateMessageData
import org.session.libsession.messaging.utilities.UpdateMessageData.Companion.fromJSON
+import org.session.libsession.utilities.Address
import org.session.libsession.utilities.Address.Companion.fromSerialized
import org.session.libsession.utilities.MediaTypes
import org.session.libsession.utilities.TextSecurePreferences
+import org.session.libsession.utilities.concurrent.SimpleTask
import org.session.libsession.utilities.recipients.Recipient
+import org.session.libsession.utilities.recipients.RecipientModifiedListener
import org.session.libsignal.utilities.ListenableFuture
-import org.session.libsignal.utilities.ThreadUtils
+import org.session.libsignal.utilities.SettableFuture
+import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.audio.AudioRecorder
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
+import org.thoughtcrime.securesms.conversation.ConversationActivity
import org.thoughtcrime.securesms.conversation.v2.dialogs.*
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate
@@ -75,7 +84,10 @@ import org.thoughtcrime.securesms.conversation.v2.input_bar.mentions.MentionCand
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallbackDelegate
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
+import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
+import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
+import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.DraftDatabase
@@ -110,8 +122,9 @@ import kotlin.math.*
// price we pay is a bit of back and forth between the input bar and the conversation activity.
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
- InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
- ConversationActionModeCallbackDelegate {
+ InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
+ ConversationActionModeCallbackDelegate, VisibleMessageContentViewDelegate, RecipientModifiedListener,
+ SearchBottomBar.EventListener {
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
private var linkPreviewViewModel: LinkPreviewViewModel? = null
private var threadID: Long = -1
@@ -130,6 +143,15 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private var previousText: CharSequence = ""
private var currentMentionStartIndex = -1
private var isShowingMentionCandidatesView = false
+ // Search
+ var searchViewModel: SearchViewModel? = null
+ var searchViewItem: MenuItem? = null
+
+ private val isScrolledToBottom: Boolean
+ get() {
+ val position = layoutManager.findFirstCompletelyVisibleItemPosition()
+ return position == 0
+ }
private val layoutManager: LinearLayoutManager
get() { return conversationRecyclerView.layoutManager as LinearLayoutManager }
@@ -150,6 +172,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
},
glide
)
+ adapter.visibleMessageContentViewDelegate = this
adapter
}
@@ -166,7 +189,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// region Settings
companion object {
+ // Extras
const val THREAD_ID = "thread_id"
+ const val ADDRESS = "address"
+ // Request codes
const val PICK_DOCUMENT = 2
const val TAKE_PHOTO = 7
const val PICK_GIF = 10
@@ -179,7 +205,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_conversation_v2)
- threadID = intent.getLongExtra(THREAD_ID, -1)
+ var threadID = intent.getLongExtra(THREAD_ID, -1L)
+ if (threadID == -1L) {
+ val address = intent.getParcelableExtra
(ADDRESS) ?: return finish()
+ val recipient = Recipient.from(this, address, false)
+ threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(recipient)
+ }
+ this.threadID = threadID
setUpRecyclerView()
setUpToolBar()
setUpInputBar()
@@ -189,12 +221,16 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
unreadCount = DatabaseFactory.getMmsSmsDatabase(this).getUnreadCount(threadID)
updateUnreadCountIndicator()
setUpTypingObserver()
+ setUpRecipientObserver()
updateSubtitle()
getLatestOpenGroupInfoIfNeeded()
setUpBlockedBanner()
setUpLinkPreviewObserver()
+ searchBottomBar.setEventListener(this)
+ setUpSearchResultObserver()
scrollToFirstUnreadMessageIfNeeded()
markAllAsRead()
+ showOrHideInputIfNeeded()
}
override fun onResume() {
@@ -251,6 +287,14 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
actionBar.setCustomView(R.layout.activity_conversation_v2_action_bar)
actionBar.setDisplayShowCustomEnabled(true)
conversationTitleView.text = thread.toShortString()
+ @DimenRes val sizeID: Int
+ if (thread.isClosedGroupRecipient) {
+ sizeID = R.dimen.medium_profile_picture_size
+ } else {
+ sizeID = R.dimen.small_profile_picture_size
+ }
+ val size = resources.getDimension(sizeID).roundToInt()
+ profilePictureView.layoutParams = LinearLayout.LayoutParams(size, size)
profilePictureView.glide = glide
profilePictureView.update(thread, threadID)
}
@@ -281,6 +325,27 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
private fun restoreDraftIfNeeded() {
+ val mediaURI = intent.data
+ val mediaType = AttachmentManager.MediaType.from(intent.type)
+ if (mediaURI != null && mediaType != null) {
+ if (AttachmentManager.MediaType.IMAGE == mediaType || AttachmentManager.MediaType.GIF == mediaType || AttachmentManager.MediaType.VIDEO == mediaType) {
+ val media = Media(mediaURI, MediaUtil.getMimeType(this, mediaURI)!!, 0, 0, 0, 0, Optional.absent(), Optional.absent())
+ startActivityForResult(MediaSendActivity.buildEditorIntent(this, listOf( media ), thread, ""), ConversationActivityV2.PICK_FROM_LIBRARY)
+ return
+ } else {
+ prepMediaForSending(mediaURI, mediaType).addListener(object : ListenableFuture.Listener {
+
+ override fun onSuccess(result: Boolean?) {
+ sendAttachments(attachmentManager.buildSlideDeck().asAttachments(), null)
+ }
+
+ override fun onFailure(e: ExecutionException?) {
+ Toast.makeText(this@ConversationActivityV2, R.string.activity_conversation_attachment_prep_failed, Toast.LENGTH_LONG).show()
+ }
+ })
+ return
+ }
+ }
val draftDB = DatabaseFactory.getDraftDatabase(this)
val drafts = draftDB.getDrafts(threadID)
draftDB.clearDrafts(threadID)
@@ -302,7 +367,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun setUpTypingObserver() {
ApplicationContext.getInstance(this).typingStatusRepository.getTypists(threadID).observe(this) { state ->
val recipients = if (state != null) state.typists else listOf()
- typingIndicatorViewContainer.isVisible = recipients.isNotEmpty()
+ // FIXME: Also checking isScrolledToBottom is a quick fix for an issue where the
+ // typing indicator overlays the recycler view when scrolled up
+ typingIndicatorViewContainer.isVisible = recipients.isNotEmpty() && isScrolledToBottom
typingIndicatorViewContainer.setTypists(recipients)
inputBarHeightChanged(inputBar.height)
}
@@ -316,6 +383,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
}
+ private fun setUpRecipientObserver() {
+ thread.addListener(this)
+ }
+
private fun getLatestOpenGroupInfoIfNeeded() {
val openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadID) ?: return
OpenGroupAPIV2.getMemberCount(openGroup.room, openGroup.server).successUi { updateSubtitle() }
@@ -358,7 +429,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
- ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, this) { onOptionsItemSelected(it) }
+ ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, threadID, this) { onOptionsItemSelected(it) }
super.onPrepareOptionsMenu(menu)
return true
}
@@ -369,7 +440,28 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
// endregion
- // region Updating & Animation
+ // region Animation & Updating
+ override fun onModified(recipient: Recipient) {
+ runOnUiThread {
+ if (thread.isContactRecipient) {
+ blockedBanner.isVisible = thread.isBlocked
+ }
+ updateSubtitle()
+ showOrHideInputIfNeeded()
+ }
+ }
+
+ private fun showOrHideInputIfNeeded() {
+ if (thread.isClosedGroupRecipient) {
+ val group = DatabaseFactory.getGroupDatabase(this).getGroup(thread.address.toGroupString()).orNull()
+ val isActive = (group?.isActive == true)
+ Log.d("Test", "isActive: $isActive")
+ inputBar.showInput = isActive
+ } else {
+ inputBar.showInput = true
+ }
+ }
+
private fun markAllAsRead() {
val messages = DatabaseFactory.getThreadDatabase(this).setRead(threadID, true)
if (thread.isGroupRecipient) {
@@ -570,8 +662,15 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
private fun handleRecyclerViewScrolled() {
- val position = layoutManager.findFirstCompletelyVisibleItemPosition()
- val alpha = if (position > 0) 1.0f else 0.0f
+ val alpha = if (!isScrolledToBottom) 1.0f else 0.0f
+ // FIXME: Checking isScrolledToBottom is a quick fix for an issue where the
+ // typing indicator overlays the recycler view when scrolled up
+ val wasTypingIndicatorVisibleBefore = typingIndicatorViewContainer.isVisible
+ typingIndicatorViewContainer.isVisible = wasTypingIndicatorVisibleBefore && isScrolledToBottom
+ val isTypingIndicatorVisibleAfter = typingIndicatorViewContainer.isVisible
+ if (isTypingIndicatorVisibleAfter != wasTypingIndicatorVisibleBefore) {
+ inputBarHeightChanged(inputBar.height)
+ }
scrollToBottomButton.alpha = alpha
unreadCount = min(unreadCount, layoutManager.findFirstVisibleItemPosition())
updateUnreadCountIndicator()
@@ -644,6 +743,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val actionMode = this.actionMode
val actionModeCallback = ConversationActionModeCallback(adapter, threadID, this)
actionModeCallback.delegate = this
+ searchViewItem?.collapseActionView()
if (actionMode == null) { // Nothing should be selected if this is the case
adapter.toggleSelection(message, position)
this.actionMode = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
@@ -740,6 +840,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
this.previousText = newText
}
+ override fun scrollToMessageIfPossible(timestamp: Long) {
+ val lastSeenItemPosition = adapter.getItemPositionForTimestamp(timestamp) ?: return
+ conversationRecyclerView.scrollToPosition(lastSeenItemPosition)
+ }
+
override fun sendMessage() {
if (thread.isContactRecipient && thread.isBlocked) {
BlockedDialog(thread).show(supportFragmentManager, "Blocked Dialog")
@@ -908,10 +1013,18 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
override fun startRecordingVoiceMessage() {
- showVoiceMessageUI()
- window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
- audioRecorder.startRecording()
- stopAudioHandler.postDelayed(stopVoiceMessageRecordingTask, 60000) // Limit voice messages to 1 minute each
+ if (Permissions.hasAll(this, Manifest.permission.RECORD_AUDIO)) {
+ showVoiceMessageUI()
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ audioRecorder.startRecording()
+ stopAudioHandler.postDelayed(stopVoiceMessageRecordingTask, 60000) // Limit voice messages to 1 minute each
+ } else {
+ Permissions.with(this)
+ .request(Manifest.permission.RECORD_AUDIO)
+ .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
+ .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
+ .execute()
+ }
}
override fun sendVoiceMessage() {
@@ -966,13 +1079,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
}
} else {
- ThreadUtils.queue {
- for (message in messages) {
- if (message.isMms) {
- DatabaseFactory.getMmsDatabase(this@ConversationActivityV2).delete(message.id)
- } else {
- DatabaseFactory.getSmsDatabase(this@ConversationActivityV2).deleteMessage(message.id)
- }
+ for (message in messages) {
+ if (message.isMms) {
+ DatabaseFactory.getMmsDatabase(this@ConversationActivityV2).delete(message.id)
+ } else {
+ DatabaseFactory.getSmsDatabase(this@ConversationActivityV2).deleteMessage(message.id)
}
}
}
@@ -1157,4 +1268,46 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
draftDB.insertDrafts(threadID, drafts)
}
// endregion
-}
+
+ // region Search
+ private fun setUpSearchResultObserver() {
+ val searchViewModel = ViewModelProvider(this).get(SearchViewModel::class.java)
+ this.searchViewModel = searchViewModel
+ searchViewModel.searchResults.observe(this, Observer { result: SearchViewModel.SearchResult? ->
+ if (result == null) return@Observer
+ if (result.getResults().isNotEmpty()) {
+ result.getResults()[result.position]?.let {
+ jumpToMessage(it.messageRecipient.address, it.receivedTimestampMs, Runnable { searchViewModel.onMissingResult() })
+ }
+ }
+ this.searchBottomBar.setData(result.position, result.getResults().size)
+ })
+ }
+
+ fun onSearchQueryUpdated(query: String?) {
+ adapter.onSearchQueryUpdated(query)
+ }
+
+ override fun onSearchMoveUpPressed() {
+ this.searchViewModel?.onMoveUp()
+ }
+
+ override fun onSearchMoveDownPressed() {
+ this.searchViewModel?.onMoveDown()
+ }
+
+ private fun jumpToMessage(author: Address, timestamp: Long, onMessageNotFound: Runnable?) {
+ SimpleTask.run(lifecycle, {
+ DatabaseFactory.getMmsSmsDatabase(this).getMessagePositionInConversation(threadID, timestamp, author)
+ }) { p: Int -> moveToMessagePosition(p, onMessageNotFound) }
+ }
+
+ private fun moveToMessagePosition(position: Int, onMessageNotFound: Runnable?) {
+ if (position >= 0) {
+ conversationRecyclerView.scrollToPosition(position)
+ } else {
+ onMessageNotFound?.run()
+ }
+ }
+ // endregion
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt
index 9b9b9e2c59..4b413ef89f 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt
@@ -9,6 +9,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import kotlinx.android.synthetic.main.view_visible_message.view.*
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
+import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
import org.thoughtcrime.securesms.database.DatabaseFactory
@@ -21,6 +22,8 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
: CursorRecyclerViewAdapter(context, cursor) {
private val messageDB = DatabaseFactory.getMmsSmsDatabase(context)
var selectedItems = mutableSetOf()
+ private var searchQuery: String? = null
+ var visibleMessageContentViewDelegate: VisibleMessageContentViewDelegate? = null
sealed class ViewType(val rawValue: Int) {
object Visible : ViewType(0)
@@ -69,10 +72,11 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
view.snIsSelected = isSelected
view.messageTimestampTextView.isVisible = isSelected
val position = viewHolder.adapterPosition
- view.bind(message, getMessageBefore(position, cursor), getMessageAfter(position, cursor), glide)
+ view.bind(message, getMessageBefore(position, cursor), getMessageAfter(position, cursor), glide, searchQuery)
view.onPress = { event -> onItemPress(message, viewHolder.adapterPosition, view, event) }
view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) }
view.onLongPress = { onItemLongPress(message, viewHolder.adapterPosition) }
+ view.contentViewDelegate = visibleMessageContentViewDelegate
}
is ControlMessageViewHolder -> viewHolder.view.bind(message)
}
@@ -114,9 +118,25 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
if (lastSeenTimestamp <= 0L || cursor == null || !isActiveCursor) return null
for (i in 0 until itemCount) {
cursor.moveToPosition(i)
- val messageRecord = messageDB.readerFor(cursor).current
- if (messageRecord.isOutgoing || messageRecord.dateReceived <= lastSeenTimestamp) { return i }
+ val message = messageDB.readerFor(cursor).current
+ if (message.isOutgoing || message.dateReceived <= lastSeenTimestamp) { return i }
}
return null
}
+
+ fun getItemPositionForTimestamp(timestamp: Long): Int? {
+ val cursor = this.cursor
+ if (timestamp <= 0L || cursor == null || !isActiveCursor) return null
+ for (i in 0 until itemCount) {
+ cursor.moveToPosition(i)
+ val message = messageDB.readerFor(cursor).current
+ if (message.dateSent == timestamp) { return i }
+ }
+ return null
+ }
+
+ fun onSearchQueryUpdated(query: String?) {
+ this.searchQuery = query
+ notifyDataSetChanged()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/ExpirationTimerView.java
similarity index 98%
rename from app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java
rename to app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/ExpirationTimerView.java
index 65cad0a274..5a04e77ac2 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/ExpirationTimerView.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/ExpirationTimerView.java
@@ -1,4 +1,4 @@
-package org.thoughtcrime.securesms.components;
+package org.thoughtcrime.securesms.conversation.v2.components;
import android.content.Context;
import androidx.annotation.NonNull;
@@ -118,5 +118,4 @@ public class ExpirationTimerView extends androidx.appcompat.widget.AppCompatImag
Util.runOnMainDelayed(this, timerView.calculateAnimationDelay(timerView.startedAt, timerView.expiresIn));
}
}
-
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt
index 4b89e9d80b..51d85c3651 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt
@@ -6,10 +6,12 @@ import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.dialog_join_open_group.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.open_groups.OpenGroupV2
import org.session.libsession.utilities.OpenGroupUrlParser
+import org.session.libsignal.utilities.ThreadUtils
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
@@ -33,8 +35,11 @@ class JoinOpenGroupDialog(private val name: String, private val url: String) : B
private fun join() {
val openGroup = OpenGroupUrlParser.parseUrl(url)
- OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, requireContext())
- MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(requireContext())
+ val activity = requireContext() as AppCompatActivity
+ ThreadUtils.queue {
+ OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, activity)
+ MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(activity)
+ }
dismiss()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt
index d77a17efca..10389ca453 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt
@@ -32,6 +32,8 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
var additionalContentHeight = 0
var quote: MessageRecord? = null
var linkPreview: LinkPreview? = null
+ var showInput: Boolean = true
+ set(value) { field = value; showOrHideInputIfNeeded() }
var text: String
get() { return inputBarEditText.text.toString() }
@@ -161,6 +163,19 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
additionalContentHeight = 0
setHeight(newHeight)
}
+
+ private fun showOrHideInputIfNeeded() {
+ if (showInput) {
+ setOf( inputBarEditText, attachmentsButton ).forEach { it.isVisible = true }
+ microphoneButton.isVisible = text.isEmpty()
+ sendButton.isVisible = text.isNotEmpty()
+ } else {
+ cancelQuoteDraft()
+ cancelLinkPreviewDraft()
+ val views = setOf( inputBarEditText, attachmentsButton, microphoneButton, sendButton )
+ views.forEach { it.isVisible = false }
+ }
+ }
// endregion
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt
index cbac59c280..5c26c4d540 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt
@@ -12,17 +12,20 @@ import android.os.AsyncTask
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
+import android.view.View
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.SearchView
+import androidx.appcompat.widget.SearchView.OnQueryTextListener
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
+import kotlinx.android.synthetic.main.activity_conversation_v2.*
import network.loki.messenger.R
-import org.session.libsession.avatars.ContactPhoto
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.sending_receiving.leave
@@ -30,11 +33,9 @@ import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient
-import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.guava.Optional
import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.*
-import org.thoughtcrime.securesms.conversation.ConversationActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity
@@ -43,11 +44,10 @@ import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.util.BitmapUtil
import java.io.IOException
-import java.lang.ref.WeakReference
object ConversationMenuHelper {
- fun onPrepareOptionsMenu(menu: Menu, inflater: MenuInflater, thread: Recipient, context: Context, onOptionsItemSelected: (MenuItem) -> Unit) {
+ fun onPrepareOptionsMenu(menu: Menu, inflater: MenuInflater, thread: Recipient, threadId: Long, context: Context, onOptionsItemSelected: (MenuItem) -> Unit) {
// Prepare
menu.clear()
val isOpenGroup = thread.isOpenGroupRecipient
@@ -92,6 +92,49 @@ object ConversationMenuHelper {
} else {
inflater.inflate(R.menu.menu_conversation_unmuted, menu)
}
+
+ // Search
+ val searchViewItem = menu.findItem(R.id.menu_search)
+ (context as ConversationActivityV2).searchViewItem = searchViewItem
+ val searchView = searchViewItem.actionView as SearchView
+ val searchViewModel = context.searchViewModel!!
+ val queryListener = object : OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String): Boolean {
+ return true
+ }
+
+ override fun onQueryTextChange(query: String): Boolean {
+ searchViewModel.onQueryUpdated(query, threadId)
+ context.searchBottomBar.showLoading()
+ context.onSearchQueryUpdated(query)
+ return true
+ }
+ }
+ searchViewItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
+ override fun onMenuItemActionExpand(item: MenuItem): Boolean {
+ searchView.setOnQueryTextListener(queryListener)
+ searchViewModel.onSearchOpened()
+ context.searchBottomBar.visibility = View.VISIBLE
+ context.searchBottomBar.setData(0, 0)
+ context.inputBar.visibility = View.GONE
+ for (i in 0 until menu.size()) {
+ if (menu.getItem(i) != searchViewItem) {
+ menu.getItem(i).isVisible = false
+ }
+ }
+ return true
+ }
+
+ override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
+ searchView.setOnQueryTextListener(null)
+ searchViewModel.onSearchClosed()
+ context.searchBottomBar.visibility = View.GONE
+ context.inputBar.visibility = View.VISIBLE
+ context.onSearchQueryUpdated(null)
+ context.invalidateOptionsMenu()
+ return true
+ }
+ })
}
fun onOptionItemSelected(context: Context, item: MenuItem, thread: Recipient): Boolean {
@@ -121,7 +164,8 @@ object ConversationMenuHelper {
}
private fun search(context: Context) {
- Toast.makeText(context, "Not yet implemented", Toast.LENGTH_LONG).show() // TODO: Implement
+ val searchViewModel = (context as ConversationActivityV2).searchViewModel!!
+ searchViewModel.onSearchOpened()
}
@SuppressLint("StaticFieldLeak")
@@ -254,7 +298,6 @@ object ConversationMenuHelper {
try {
if (isClosedGroup) {
MessageSender.leave(groupPublicKey!!, true)
- // TODO: Disable input?
} else {
Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show()
}
@@ -274,13 +317,11 @@ object ConversationMenuHelper {
}
private fun unmute(context: Context, thread: Recipient) {
- thread.setMuted(0)
DatabaseFactory.getRecipientDatabase(context).setMuted(thread, 0)
}
private fun mute(context: Context, thread: Recipient) {
MuteDialog.show(context) { until: Long ->
- thread.setMuted(until)
DatabaseFactory.getRecipientDatabase(context).setMuted(thread, until)
}
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt
index b4f3810e16..78e926d041 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt
@@ -32,6 +32,9 @@ class ControlMessageView : LinearLayout {
if (message.isExpirationTimerUpdate) {
iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_timer, context.theme))
iconImageView.visibility = View.VISIBLE
+ } else if (message.isMediaSavedNotification) {
+ iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_file_download_white_36dp, context.theme))
+ iconImageView.visibility = View.VISIBLE
}
textView.text = message.getDisplayBody(context)
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt
index 8658dfae81..8f0e61b381 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt
@@ -42,7 +42,7 @@ class LinkPreviewView : LinearLayout {
// endregion
// region Updating
- fun bind(message: MmsMessageRecord, glide: GlideRequests, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean) {
+ fun bind(message: MmsMessageRecord, glide: GlideRequests, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean, searchQuery: String?) {
val linkPreview = message.linkPreviews.first()
url = linkPreview.url
// Thumbnail
@@ -60,7 +60,7 @@ class LinkPreviewView : LinearLayout {
}
titleTextView.setTextColor(ResourcesCompat.getColor(resources, textColorID, context.theme))
// Body
- bodyTextView = VisibleMessageContentView.getBodyTextView(context, message)
+ bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery)
mainLinkPreviewContainer.addView(bodyTextView)
// Corner radii
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt
index 14d62945a7..16721b1625 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt
@@ -1,20 +1,20 @@
package org.thoughtcrime.securesms.conversation.v2.messages
import android.content.Context
+import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.Drawable
+import android.text.style.BackgroundColorSpan
+import android.text.style.ForegroundColorSpan
import android.text.method.LinkMovementMethod
-import android.text.style.ReplacementSpan
import android.text.style.URLSpan
import android.text.util.Linkify
import android.util.AttributeSet
-import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.MotionEvent
import android.widget.LinearLayout
import android.widget.TextView
-import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
@@ -24,6 +24,7 @@ import androidx.core.graphics.BlendModeCompat
import androidx.core.text.getSpans
import androidx.core.text.toSpannable
import androidx.core.text.util.LinkifyCompat
+import kotlinx.android.synthetic.main.view_link_preview.view.*
import kotlinx.android.synthetic.main.view_visible_message_content.view.*
import network.loki.messenger.R
import org.session.libsession.utilities.ThemeUtil
@@ -38,11 +39,15 @@ import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.loki.utilities.*
import org.thoughtcrime.securesms.mms.GlideRequests
+import org.thoughtcrime.securesms.util.SearchUtil
+import org.thoughtcrime.securesms.util.SearchUtil.StyleFactory
+import java.util.*
import kotlin.math.roundToInt
class VisibleMessageContentView : LinearLayout {
var onContentClick: ((event: MotionEvent) -> Unit)? = null
var onContentDoubleTap: (() -> Unit)? = null
+ var delegate: VisibleMessageContentViewDelegate? = null
// region Lifecycle
constructor(context: Context) : super(context) { initialize() }
@@ -56,7 +61,7 @@ class VisibleMessageContentView : LinearLayout {
// region Updating
fun bind(message: MessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean,
- glide: GlideRequests, maxWidth: Int, thread: Recipient) {
+ glide: GlideRequests, maxWidth: Int, thread: Recipient, searchQuery: String?) {
// Background
val background = getBackground(message.isOutgoing, isStartOfMessageCluster, isEndOfMessageCluster)
val colorID = if (message.isOutgoing) R.attr.message_sent_background_color else R.attr.message_received_background_color
@@ -70,7 +75,7 @@ class VisibleMessageContentView : LinearLayout {
onContentDoubleTap = null
if (message is MmsMessageRecord && message.linkPreviews.isNotEmpty()) {
val linkPreviewView = LinkPreviewView(context)
- linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster)
+ linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster, searchQuery)
mainContainer.addView(linkPreviewView)
onContentClick = { event -> linkPreviewView.calculateHit(event) }
// Body text view is inside the link preview for layout convenience
@@ -84,9 +89,16 @@ class VisibleMessageContentView : LinearLayout {
quoteView.bind(quote.author.toString(), quote.text, quote.attachment, thread,
message.isOutgoing, maxContentWidth, message.isOpenGroupInvitation, message.threadId, glide)
mainContainer.addView(quoteView)
- val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message)
+ val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery)
ViewUtil.setPaddingTop(bodyTextView, 0)
mainContainer.addView(bodyTextView)
+ onContentClick = { event ->
+ val r = Rect()
+ quoteView.getGlobalVisibleRect(r)
+ if (r.contains(event.rawX.roundToInt(), event.rawY.roundToInt())) {
+ delegate?.scrollToMessageIfPossible(quote.id)
+ }
+ }
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
val voiceMessageView = VoiceMessageView(context)
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
@@ -119,7 +131,7 @@ class VisibleMessageContentView : LinearLayout {
mainContainer.addView(openGroupInvitationView)
onContentClick = { openGroupInvitationView.joinOpenGroup() }
} else {
- val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message)
+ val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery)
mainContainer.addView(bodyTextView)
onContentClick = { event ->
// intersectedModalSpans should only be a list of one item
@@ -153,7 +165,7 @@ class VisibleMessageContentView : LinearLayout {
// region Convenience
companion object {
- fun getBodyTextView(context: Context, message: MessageRecord): TextView {
+ fun getBodyTextView(context: Context, message: MessageRecord, searchQuery: String?): TextView {
val result = EmojiTextView(context)
val vPadding = context.resources.getDimension(R.dimen.small_spacing).toInt()
val hPadding = toPx(12, context.resources)
@@ -177,8 +189,11 @@ class VisibleMessageContentView : LinearLayout {
body.removeSpan(urlSpan)
body.setSpan(replacementSpan, start, end, flags)
}
-
+
body = MentionUtilities.highlightMentions(body, message.isOutgoing, message.threadId, context)
+ body = SearchUtil.getHighlightedSpan(Locale.getDefault(), StyleFactory { BackgroundColorSpan(Color.WHITE) }, body, searchQuery)
+ body = SearchUtil.getHighlightedSpan(Locale.getDefault(), StyleFactory { ForegroundColorSpan(Color.BLACK) }, body, searchQuery)
+
result.text = body
return result
}
@@ -195,4 +210,9 @@ class VisibleMessageContentView : LinearLayout {
}
}
// endregion
+}
+
+interface VisibleMessageContentViewDelegate {
+
+ fun scrollToMessageIfPossible(timestamp: Long)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt
index 356a9e2fbb..93f0236f36 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt
@@ -3,21 +3,27 @@ package org.thoughtcrime.securesms.conversation.v2.messages
import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
+import android.graphics.ColorFilter
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
+import android.os.AsyncTask
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.*
import android.widget.LinearLayout
+import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
+import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.view_visible_message.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact.ContactContext
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.utilities.ViewUtil
+import org.session.libsignal.utilities.ThreadUtils
+import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
@@ -47,6 +53,7 @@ class VisibleMessageView : LinearLayout {
var onPress: ((event: MotionEvent) -> Unit)? = null
var onSwipeToReply: (() -> Unit)? = null
var onLongPress: (() -> Unit)? = null
+ var contentViewDelegate: VisibleMessageContentViewDelegate? = null
companion object {
const val swipeToReplyThreshold = 80.0f // dp
@@ -69,7 +76,7 @@ class VisibleMessageView : LinearLayout {
// endregion
// region Updating
- fun bind(message: MessageRecord, previous: MessageRecord?, next: MessageRecord?, glide: GlideRequests) {
+ fun bind(message: MessageRecord, previous: MessageRecord?, next: MessageRecord?, glide: GlideRequests, searchQuery: String?) {
val sender = message.individualRecipient
val senderSessionID = sender.address.serialize()
val threadID = message.threadId
@@ -137,11 +144,14 @@ class VisibleMessageView : LinearLayout {
} else {
messageStatusImageView.isVisible = false
}
+ // Expiration timer
+ updateExpirationTimer(message)
// Calculate max message bubble width
var maxWidth = screenWidth - messageContentContainerLayoutParams.leftMargin - messageContentContainerLayoutParams.rightMargin
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
// Populate content view
- messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread)
+ messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery)
+ messageContentView.delegate = contentViewDelegate
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
}
@@ -182,6 +192,37 @@ class VisibleMessageView : LinearLayout {
}
}
+ private fun updateExpirationTimer(message: MessageRecord) {
+ val expirationTimerViewLayoutParams = expirationTimerView.layoutParams as RelativeLayout.LayoutParams
+ val ruleToAdd = if (message.isOutgoing) RelativeLayout.ALIGN_PARENT_START else RelativeLayout.ALIGN_PARENT_END
+ val ruleToRemove = if (message.isOutgoing) RelativeLayout.ALIGN_PARENT_END else RelativeLayout.ALIGN_PARENT_START
+ expirationTimerViewLayoutParams.removeRule(ruleToRemove)
+ expirationTimerViewLayoutParams.addRule(ruleToAdd)
+ expirationTimerView.layoutParams = expirationTimerViewLayoutParams
+ if (message.expiresIn > 0 && !message.isPending) {
+ expirationTimerView.setColorFilter(ResourcesCompat.getColor(resources, R.color.text, context.theme))
+ expirationTimerView.isVisible = true
+ expirationTimerView.setPercentComplete(0.0f)
+ if (message.expireStarted > 0) {
+ expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
+ expirationTimerView.startAnimation()
+ if (message.expireStarted + message.expiresIn <= System.currentTimeMillis()) {
+ ApplicationContext.getInstance(context).expiringMessageManager.checkSchedule()
+ }
+ } else if (!message.isOutgoing && !message.isMediaPending) {
+ ThreadUtils.queue {
+ val expirationManager = ApplicationContext.getInstance(context).expiringMessageManager
+ val id = message.getId()
+ val mms = message.isMms
+ if (mms) DatabaseFactory.getMmsDatabase(context).markExpireStarted(id) else DatabaseFactory.getSmsDatabase(context).markExpireStarted(id)
+ expirationManager.scheduleDeletion(id, mms, message.expiresIn)
+ }
+ }
+ } else {
+ expirationTimerView.isVisible = false
+ }
+ }
+
private fun handleIsSelectedChanged() {
background = if (snIsSelected) {
ColorDrawable(context.resources.getColorWithID(R.color.message_selected, context.theme))
@@ -242,6 +283,7 @@ class VisibleMessageView : LinearLayout {
} else {
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
}
+ if (translationX > 0) { return } // Only allow swipes to the left
// The idea here is to asymptotically approach a maximum drag distance
val damping = 50.0f
val sign = -1.0f
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/search/SearchBottomBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/search/SearchBottomBar.kt
new file mode 100644
index 0000000000..da8df0045a
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/search/SearchBottomBar.kt
@@ -0,0 +1,62 @@
+package org.thoughtcrime.securesms.conversation.v2.search
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import kotlinx.android.synthetic.main.view_search_bottom_bar.view.*
+import network.loki.messenger.R
+
+
+class SearchBottomBar : LinearLayout {
+ private var eventListener: EventListener? = null
+
+ // region Lifecycle
+ constructor(context: Context) : super(context) { initialize() }
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
+ constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
+
+ fun initialize() {
+ LayoutInflater.from(context).inflate(R.layout.view_search_bottom_bar, this)
+ }
+
+ fun setData(position: Int, count: Int) {
+ searchProgressWheel.visibility = GONE
+ searchUp.setOnClickListener { v: View? ->
+ if (eventListener != null) {
+ eventListener!!.onSearchMoveUpPressed()
+ }
+ }
+ searchDown.setOnClickListener { v: View? ->
+ if (eventListener != null) {
+ eventListener!!.onSearchMoveDownPressed()
+ }
+ }
+ if (count > 0) {
+ searchPosition.text = resources.getString(R.string.ConversationActivity_search_position, position + 1, count)
+ } else {
+ searchPosition.text = ""
+ }
+ setViewEnabled(searchUp, position < count - 1)
+ setViewEnabled(searchDown, position > 0)
+ }
+
+ fun showLoading() {
+ searchProgressWheel.visibility = VISIBLE
+ }
+
+ private fun setViewEnabled(view: View, enabled: Boolean) {
+ view.isEnabled = enabled
+ view.alpha = if (enabled) 1f else 0.25f
+ }
+
+ fun setEventListener(eventListener: EventListener?) {
+ this.eventListener = eventListener
+ }
+
+ interface EventListener {
+ fun onSearchMoveUpPressed()
+ fun onSearchMoveDownPressed()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/search/SearchViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/search/SearchViewModel.kt
new file mode 100644
index 0000000000..eb3dd50d98
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/search/SearchViewModel.kt
@@ -0,0 +1,111 @@
+package org.thoughtcrime.securesms.conversation.v2.search
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import org.session.libsession.utilities.Debouncer
+import org.session.libsession.utilities.Util.runOnMain
+import org.session.libsession.utilities.concurrent.SignalExecutors
+import org.thoughtcrime.securesms.contacts.ContactAccessor
+import org.thoughtcrime.securesms.database.CursorList
+import org.thoughtcrime.securesms.database.DatabaseFactory
+import org.thoughtcrime.securesms.search.SearchRepository
+import org.thoughtcrime.securesms.search.model.MessageResult
+import org.thoughtcrime.securesms.util.CloseableLiveData
+import java.io.Closeable
+
+
+class SearchViewModel(application: Application) : AndroidViewModel(application) {
+ private val searchRepository: SearchRepository
+ private val result: CloseableLiveData
+ private val debouncer: Debouncer
+ private var firstSearch = false
+ private var searchOpen = false
+ private var activeQuery: String? = null
+ private var activeThreadId: Long = 0
+ val searchResults: LiveData
+ get() = result
+
+ fun onQueryUpdated(query: String, threadId: Long) {
+ if (query == activeQuery) {
+ return
+ }
+ updateQuery(query, threadId)
+ }
+
+ fun onMissingResult() {
+ if (activeQuery != null) {
+ updateQuery(activeQuery!!, activeThreadId)
+ }
+ }
+
+ fun onMoveUp() {
+ debouncer.clear()
+ val messages = result.value!!.getResults() as CursorList
+ val position = Math.min(result.value!!.position + 1, messages.size - 1)
+ result.setValue(SearchResult(messages, position), false)
+ }
+
+ fun onMoveDown() {
+ debouncer.clear()
+ val messages = result.value!!.getResults() as CursorList
+ val position = Math.max(result.value!!.position - 1, 0)
+ result.setValue(SearchResult(messages, position), false)
+ }
+
+ fun onSearchOpened() {
+ searchOpen = true
+ firstSearch = true
+ }
+
+ fun onSearchClosed() {
+ searchOpen = false
+ activeQuery = null
+ debouncer.clear()
+ result.close()
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ result.close()
+ }
+
+ private fun updateQuery(query: String, threadId: Long) {
+ activeQuery = query
+ activeThreadId = threadId
+ debouncer.publish {
+ firstSearch = false
+ searchRepository.query(query, threadId) { messages: CursorList ->
+ runOnMain {
+ if (searchOpen && query == activeQuery) {
+ result.setValue(SearchResult(messages, 0))
+ } else {
+ messages.close()
+ }
+ }
+ }
+ }
+ }
+
+ class SearchResult(private val results: CursorList, val position: Int) : Closeable {
+
+ fun getResults(): List {
+ return results
+ }
+
+ override fun close() {
+ results.close()
+ }
+ }
+
+ init {
+ val context = application.applicationContext
+ result = CloseableLiveData()
+ debouncer = Debouncer(500)
+ searchRepository = SearchRepository(context,
+ DatabaseFactory.getSearchDatabase(context),
+ DatabaseFactory.getThreadDatabase(context),
+ ContactAccessor.getInstance(),
+ SignalExecutors.SERIAL)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java
index ed443591f0..ce2ddba24a 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -57,6 +57,7 @@ import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsession.utilities.recipients.RecipientFormattingException;
import org.session.libsignal.utilities.JsonUtil;
import org.session.libsignal.utilities.Log;
+import org.session.libsignal.utilities.ThreadUtils;
import org.session.libsignal.utilities.guava.Optional;
import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
@@ -881,9 +882,9 @@ public class MmsDatabase extends MessagingDatabase {
}
public boolean delete(long messageId) {
- long threadId = getThreadIdForMessage(messageId);
+ long threadId = getThreadIdForMessage(messageId);
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
- attachmentDatabase.deleteAttachmentsForMessage(messageId);
+ ThreadUtils.queue(() -> attachmentDatabase.deleteAttachmentsForMessage(messageId));
GroupReceiptDatabase groupReceiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
groupReceiptDatabase.deleteRowsForMessage(messageId);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java
index d241db9862..6706f5fe77 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java
@@ -20,6 +20,8 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.os.Handler;
+import android.os.Looper;
import android.text.TextUtils;
import android.util.Pair;
@@ -411,7 +413,6 @@ public class SmsDatabase extends MessagingDatabase {
notifyConversationListeners(threadId);
-
return Optional.of(new InsertResult(messageId, threadId));
}
}
@@ -512,7 +513,7 @@ public class SmsDatabase extends MessagingDatabase {
public boolean deleteMessage(long messageId) {
Log.i("MessageDatabase", "Deleting: " + messageId);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
- long threadId = getThreadIdForMessage(messageId);
+ long threadId = getThreadIdForMessage(messageId);
db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
boolean threadDeleted = DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
index 75db53493b..15b91048e2 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.database
-import android.app.job.JobScheduler
import android.content.Context
import android.net.Uri
import org.session.libsession.database.StorageProtocol
@@ -106,7 +105,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
else -> Optional.absent()
}
- val pointerAttachments = attachments.mapNotNull {
+ val pointers = attachments.mapNotNull {
it.toSignalAttachment()
}
val targetAddress = if (isUserSender && !message.syncTarget.isNullOrEmpty()) {
@@ -122,7 +121,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val linkPreviews: Optional> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! })
val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
val insertResult = if (message.sender == getUserPublicKey()) {
- val mediaMessage = OutgoingMediaMessage.from(message, targetRecipient, pointerAttachments, quote.orNull(), linkPreviews.orNull()?.firstOrNull())
+ val mediaMessage = OutgoingMediaMessage.from(message, targetRecipient, pointers, quote.orNull(), linkPreviews.orNull()?.firstOrNull())
mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!)
} else {
// It seems like we have replaced SignalServiceAttachment with SessionServiceAttachment
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt
index f13dee1d46..df4f3ce614 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt
@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.loki.utilities.fadeOut
import org.thoughtcrime.securesms.mms.GlideApp
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.TextSecurePreferences
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
//TODO Refactor to avoid using kotlinx.android.synthetic
class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks> {
@@ -135,10 +136,10 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM
// region Convenience
private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) {
- val intent = Intent(context, ConversationActivity::class.java)
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId)
+ val intent = Intent(context, ConversationActivityV2::class.java)
+ intent.putExtra(ConversationActivityV2.THREAD_ID, threadId)
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address)
+ intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
context.startActivity(intent)
}
// endregion
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt
index 7f75b9cc77..7d429bf223 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt
@@ -30,6 +30,7 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.ConversationActivity
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
@@ -111,12 +112,12 @@ class CreatePrivateChatActivity : PassphraseRequiredActionBarActivity(), ScanQRC
private fun createPrivateChat(hexEncodedPublicKey: String) {
val recipient = Recipient.from(this, Address.fromSerialized(hexEncodedPublicKey), false)
- val intent = Intent(this, ConversationActivity::class.java)
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address)
+ val intent = Intent(this, ConversationActivityV2::class.java)
+ intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA))
intent.setDataAndType(getIntent().data, getIntent().type)
val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient)
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread)
+ intent.putExtra(ConversationActivityV2.THREAD_ID, existingThread)
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
startActivity(intent)
finish()
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt
index deec2bf434..dc038cc0cb 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt
@@ -33,6 +33,7 @@ import org.session.libsignal.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.ConversationActivity
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
@@ -127,10 +128,10 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
// region Convenience
private fun openConversationActivity(context: Context, threadId: Long, recipient: Recipient) {
- val intent = Intent(context, ConversationActivity::class.java)
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId)
+ val intent = Intent(context, ConversationActivityV2::class.java)
+ intent.putExtra(ConversationActivityV2.THREAD_ID, threadId)
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address)
+ intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
context.startActivity(intent)
}
// endregion
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt
index dd6bf3420e..96d175ef99 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt
@@ -26,6 +26,7 @@ import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.util.FileProviderUtil
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.PublicKeyValidation
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import java.io.File
import java.io.FileOutputStream
@@ -53,12 +54,12 @@ class QRCodeActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperF
fun createPrivateChatIfPossible(hexEncodedPublicKey: String) {
if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { return Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() }
val recipient = Recipient.from(this, Address.fromSerialized(hexEncodedPublicKey), false)
- val intent = Intent(this, ConversationActivity::class.java)
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.address)
+ val intent = Intent(this, ConversationActivityV2::class.java)
+ intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA))
intent.setDataAndType(getIntent().data, getIntent().type)
val existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient)
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread)
+ intent.putExtra(ConversationActivityV2.THREAD_ID, existingThread)
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, DistributionTypes.DEFAULT)
startActivity(intent)
finish()
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt
index 3482088dd9..b07675f5ea 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt
@@ -31,23 +31,12 @@ class ProfilePictureView : RelativeLayout {
private val profilePicturesCache = mutableMapOf()
// region Lifecycle
- constructor(context: Context) : super(context) {
- setUpViewHierarchy()
- }
+ constructor(context: Context) : super(context) { initialize() }
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
+ constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
+ constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { initialize() }
- constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
- setUpViewHierarchy()
- }
-
- constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
- setUpViewHierarchy()
- }
-
- constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
- setUpViewHierarchy()
- }
-
- private fun setUpViewHierarchy() {
+ private fun initialize() {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_profile_picture, null)
addView(contentView)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java
index 532c953152..a0c1046e07 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java
@@ -49,6 +49,7 @@ import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
@@ -115,9 +116,9 @@ public class DefaultMessageNotifier implements MessageNotifier {
if (visibleThread == threadId) {
sendInThreadNotification(context, recipient);
} else {
- Intent intent = new Intent(context, ConversationActivity.class);
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
+ Intent intent = new Intent(context, ConversationActivityV2.class);
+ intent.putExtra(ConversationActivityV2.ADDRESS, recipient.getAddress());
+ intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
intent.setData((Uri.parse("custom://" + System.currentTimeMillis())));
FailedNotificationBuilder builder = new FailedNotificationBuilder(context, TextSecurePreferences.getNotificationPrivacy(context), intent);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java
index e92f62aa44..c2041a84fa 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java
@@ -9,6 +9,7 @@ import androidx.annotation.Nullable;
import androidx.core.app.TaskStackBuilder;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.session.libsession.utilities.recipients.Recipient;
@@ -67,11 +68,11 @@ public class NotificationItem {
}
public PendingIntent getPendingIntent(Context context) {
- Intent intent = new Intent(context, ConversationActivity.class);
+ Intent intent = new Intent(context, ConversationActivityV2.class);
Recipient notifyRecipients = threadRecipient != null ? threadRecipient : conversationRecipient;
- if (notifyRecipients != null) intent.putExtra(ConversationActivity.ADDRESS_EXTRA, notifyRecipients.getAddress());
+ if (notifyRecipients != null) intent.putExtra(ConversationActivityV2.ADDRESS, notifyRecipients.getAddress());
- intent.putExtra("thread_id", threadId);
+ intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
return TaskStackBuilder.create(context)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java
index 418de713cb..e8dd7cd589 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java
@@ -14,6 +14,7 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import network.loki.messenger.R;
+import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.session.libsession.utilities.recipients.Recipient;
@@ -32,9 +33,9 @@ public class CommunicationActions {
@Override
protected void onPostExecute(Long threadId) {
- Intent intent = new Intent(context, ConversationActivity.class);
- intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
- intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
+ Intent intent = new Intent(context, ConversationActivityV2.class);
+ intent.putExtra(ConversationActivityV2.ADDRESS, recipient.getAddress());
+ intent.putExtra(ConversationActivityV2.THREAD_ID, threadId);
intent.putExtra(ConversationActivity.TIMING_EXTRA, System.currentTimeMillis());
if (!TextUtils.isEmpty(text)) {
diff --git a/app/src/main/res/layout/activity_conversation_v2.xml b/app/src/main/res/layout/activity_conversation_v2.xml
index 66d71f5cce..079c932e99 100644
--- a/app/src/main/res/layout/activity_conversation_v2.xml
+++ b/app/src/main/res/layout/activity_conversation_v2.xml
@@ -34,6 +34,13 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
+
+
-
+ android:layout_width="@dimen/medium_profile_picture_size"
+ android:layout_height="@dimen/medium_profile_picture_size" />
-
+ android:layout_marginBottom="@dimen/small_spacing"
+ app:tint="@color/text" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_visible_message.xml b/app/src/main/res/layout/view_visible_message.xml
index 7148d07a05..b3d258c8ea 100644
--- a/app/src/main/res/layout/view_visible_message.xml
+++ b/app/src/main/res/layout/view_visible_message.xml
@@ -1,114 +1,126 @@
-
-
-
+ android:layout_height="wrap_content">
+ android:orientation="vertical">
+
+
+ android:orientation="horizontal"
+ android:gravity="bottom">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:orientation="horizontal">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ android:ellipsize="end" />
-
+
-
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
+
+
\ No newline at end of file