diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c89fca8a70..d34a36f1b2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -230,11 +230,13 @@
android:name="org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2"
android:screenOrientation="portrait"
android:parentActivityName="org.thoughtcrime.securesms.home.HomeActivity"
- android:theme="@style/Theme.Session.DayNight.NoActionBar">
+ android:theme="@style/Theme.Session.DayNight.NoActionBar"
+ android:windowSoftInputMode="adjustResize" >
+
- showScrollToBottomButtonIfApplicable()
+ private fun scrollToMostRecentMessageIfWeShould() {
+ // Grab an initial 'previous' last visible message..
+ if (previousLastVisibleRecyclerViewIndex == RecyclerView.NO_POSITION) {
+ previousLastVisibleRecyclerViewIndex = layoutManager?.findLastVisibleItemPosition()!!
}
+
+ // ..and grab the 'current' last visible message.
+ currentLastVisibleRecyclerViewIndex = layoutManager?.findLastVisibleItemPosition()!!
+
+ // If the current last visible message index is less than the previous one (i.e. we've
+ // lost visibility of one or more messages due to showing the IME keyboard) AND we're
+ // at the bottom of the message feed..
+ val atBottomAndTrueLastNoLongerVisible = currentLastVisibleRecyclerViewIndex!! <= previousLastVisibleRecyclerViewIndex!! && !binding?.scrollToBottomButton?.isVisible!!
+
+ // ..OR we're at the last message or have received a new message..
+ val atLastOrReceivedNewMessage = currentLastVisibleRecyclerViewIndex == (adapter.itemCount - 1)
+
+ // ..then scroll the recycler view to the last message on resize. Note: We cannot just call
+ // scroll/smoothScroll - we have to `post` it or nothing happens!
+ if (atBottomAndTrueLastNoLongerVisible || atLastOrReceivedNewMessage) {
+ binding?.conversationRecyclerView?.post {
+ binding?.conversationRecyclerView?.smoothScrollToPosition(adapter.itemCount)
+ }
+ }
+
+ // Update our previous last visible view index to the current one
+ previousLastVisibleRecyclerViewIndex = currentLastVisibleRecyclerViewIndex
}
// called from onCreate
@@ -760,13 +806,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// of the first unread message in the middle of the screen
if (isFirstLoad && !reverseMessageList) {
layoutManager?.scrollToPositionWithOffset(lastSeenItemPosition, ((layoutManager?.height ?: 0) / 2))
-
if (shouldHighlight) { highlightViewAtPosition(lastSeenItemPosition) }
-
return lastSeenItemPosition
}
if (lastSeenItemPosition <= 3) { return lastSeenItemPosition }
+
binding?.conversationRecyclerView?.scrollToPosition(lastSeenItemPosition)
return lastSeenItemPosition
}
@@ -1040,8 +1085,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun handleRecyclerViewScrolled() {
val binding = binding ?: return
+
+ // Note: The typing indicate is whether the other person / other people are typing - it has
+ // nothing to do with the IME keyboard state.
val wasTypingIndicatorVisibleBefore = binding.typingIndicatorViewContainer.isVisible
binding.typingIndicatorViewContainer.isVisible = wasTypingIndicatorVisibleBefore && isScrolledToBottom
+
showScrollToBottomButtonIfApplicable()
val maybeTargetVisiblePosition = if (reverseMessageList) layoutManager?.findFirstVisibleItemPosition() else layoutManager?.findLastVisibleItemPosition()
val targetVisiblePosition = maybeTargetVisiblePosition ?: RecyclerView.NO_POSITION
@@ -2107,4 +2156,15 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
}
+ // AdapterDataObserver implementation to scroll us to the bottom of the ConversationRecyclerView
+ // when we're already near the bottom and we send or receive a message.
+ inner class ConversationAdapterDataObserver(val recyclerView: ConversationRecyclerView, val adapter: ConversationAdapter) : RecyclerView.AdapterDataObserver() {
+ override fun onChanged() {
+ super.onChanged()
+ if (recyclerView.isScrolledToWithin30dpOfBottom) {
+ recyclerView.scrollToPosition(adapter.itemCount-1)
+ }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt
index a38c93831e..9124765763 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt
@@ -37,3 +37,8 @@ val RecyclerView.isScrolledToBottom: Boolean
get() = computeVerticalScrollOffset().coerceAtLeast(0) +
computeVerticalScrollExtent() +
toPx(50, resources) >= computeVerticalScrollRange()
+
+val RecyclerView.isScrolledToWithin30dpOfBottom: Boolean
+ get() = computeVerticalScrollOffset().coerceAtLeast(0) +
+ computeVerticalScrollExtent() +
+ toPx(30, resources) >= computeVerticalScrollRange()
\ No newline at end of file
diff --git a/app/src/main/res/drawable/cross.xml b/app/src/main/res/drawable/cross.xml
new file mode 100644
index 0000000000..5b090de2b3
--- /dev/null
+++ b/app/src/main/res/drawable/cross.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_conversation_v2.xml b/app/src/main/res/layout/activity_conversation_v2.xml
index 000b860841..6fe0c4db60 100644
--- a/app/src/main/res/layout/activity_conversation_v2.xml
+++ b/app/src/main/res/layout/activity_conversation_v2.xml
@@ -23,6 +23,10 @@
+
+