aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/main/java/de/pixart/messenger/entities/Conversation.java19
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java82
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java77
-rw-r--r--src/main/res/drawable/ic_scroll_to_end_black.xml9
-rw-r--r--src/main/res/drawable/ic_scroll_to_end_white.xml38
-rw-r--r--src/main/res/layout/fragment_conversation.xml31
-rw-r--r--src/main/res/values/attrs.xml5
-rw-r--r--src/main/res/values/themes.xml4
8 files changed, 252 insertions, 13 deletions
diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java
index fa5e5ef60..8b3f06711 100644
--- a/src/main/java/de/pixart/messenger/entities/Conversation.java
+++ b/src/main/java/de/pixart/messenger/entities/Conversation.java
@@ -1110,6 +1110,25 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
&& sentMessagesCount() == 0;
}
+ public int getReceivedMessagesCountSinceUuid(String uuid) {
+ if (uuid == null) {
+ return 0;
+ }
+ int count = 0;
+ synchronized (this.messages) {
+ for (int i = messages.size() - 1; i >= 0; i--) {
+ final Message message = messages.get(i);
+ if (uuid.equals(message.getUuid())) {
+ return count;
+ }
+ if (message.getStatus() <= Message.STATUS_RECEIVED) {
+ ++count;
+ }
+ }
+ }
+ return 0;
+ }
+
public interface OnMessageFound {
void onMessageFound(final Message message);
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index 8a1db5736..b94b80d2a 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -141,14 +141,18 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
public static final String STATE_SCROLL_POSITION = ConversationFragment.class.getName() + ".scroll_position";
public static final String STATE_PHOTO_URI = ConversationFragment.class.getName() + ".take_photo_uri";
+ private static final String STATE_LAST_MESSAGE_UUID = "state_last_message_uuid";
+
final protected List<Message> messageList = new ArrayList<>();
final private List<Uri> mPendingImageUris = new ArrayList<>();
+ private String lastMessageUuid = null;
public Uri mPendingEditorContent = null;
private final PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
private final PendingItem<String> pendingConversationsUuid = new PendingItem<>();
private final PendingItem<Bundle> pendingExtras = new PendingItem<>();
private final PendingItem<Uri> pendingTakePhotoUri = new PendingItem<>();
private final PendingItem<ScrollState> pendingScrollState = new PendingItem<>();
+ private final PendingItem<String> pendingLastMessageUuid = new PendingItem<>();
private final PendingItem<Message> pendingMessage = new PendingItem<>();
protected MessageAdapter messageListAdapter;
private Conversation conversation;
@@ -206,6 +210,27 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
});
}
};
+
+ private void toggleScrollDownButton() {
+ toggleScrollDownButton(binding.messagesView, binding.messagesView.getCount());
+ }
+
+ private void toggleScrollDownButton(AbsListView listView, int count) {
+ if (listView.getLastVisiblePosition() < count - 5) {
+ binding.scrollToBottomButton.setEnabled(true);
+ binding.scrollToBottomButton.setVisibility(View.VISIBLE);
+ if (lastMessageUuid == null) {
+ lastMessageUuid = conversation.getLatestMessage().getUuid();
+ }
+ if (conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid) > 0) {
+ binding.unreadCountCustomView.setVisibility(View.VISIBLE);
+ }
+ } else if (scrolledToBottom(listView)) {
+ lastMessageUuid = null;
+ hideUnreadMessagesCount();
+ }
+ }
+
private OnScrollListener mOnScrollListener = new OnScrollListener() {
@Override
@@ -215,8 +240,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
@Override
- public void onScroll(final AbsListView view, int firstVisibleItem,
- int visibleItemCount, final int totalItemCount) {
+ public void onScroll(final AbsListView view, int firstVisibleItem, int visibleItemCount, final int totalItemCount) {
+ toggleScrollDownButton(view, totalItemCount);
synchronized (ConversationFragment.this.messageList) {
if (firstVisibleItem < 25 && conversation != null && conversation.messagesLoaded.compareAndSet(true, false) && messageList.size() > 0) {
long timestamp;
@@ -414,6 +439,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
};
+ private OnClickListener mScrollButtonListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ stopScrolling();
+ setSelection(binding.messagesView.getCount() - 1);
+ }
+ };
+
private OnClickListener mSendButtonListener = new OnClickListener() {
@Override
@@ -647,9 +681,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
- private void setScrollPosition(ScrollState scrollPosition) {
+ private void setScrollPosition(ScrollState scrollPosition, String lastMessageUuid) {
if (scrollPosition != null) {
+ this.lastMessageUuid = lastMessageUuid;
+ if (lastMessageUuid != null) {
+ binding.unreadCountCustomView.setUnreadCount(conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid));
+ }
+ //TODO maybe this needs a 'post'
this.binding.messagesView.setSelectionFromTop(scrollPosition.position, scrollPosition.offset);
+ toggleScrollDownButton();
}
}
@@ -1129,7 +1169,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- this.binding = DataBindingUtil.inflate(inflater,R.layout.fragment_conversation,container,false);
+ this.binding = DataBindingUtil.inflate(inflater, R.layout.fragment_conversation, container, false);
binding.getRoot().setOnClickListener(null); //TODO why the fuck did we do this?
binding.textinput.addTextChangedListener(new StylingHelper.MessageEditorStyler(binding.textinput));
@@ -1138,6 +1178,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
binding.textSendButton.setOnClickListener(this.mSendButtonListener);
binding.textSendButton.setOnLongClickListener(this.mSendButtonLongListener);
+ binding.scrollToBottomButton.setOnClickListener(this.mScrollButtonListener);
binding.messagesView.setOnScrollListener(mOnScrollListener);
binding.messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
@@ -1965,6 +2006,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
super.onSaveInstanceState(outState);
if (conversation != null) {
outState.putString(STATE_CONVERSATION_UUID, conversation.getUuid());
+ outState.putString(STATE_LAST_MESSAGE_UUID, lastMessageUuid);
final Uri uri = pendingTakePhotoUri.peek();
if (uri != null) {
outState.putString(STATE_PHOTO_URI, uri.toString());
@@ -1983,6 +2025,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return;
}
String uuid = savedInstanceState.getString(STATE_CONVERSATION_UUID);
+ pendingLastMessageUuid.push(savedInstanceState.getString(STATE_LAST_MESSAGE_UUID, null));
if (uuid != null) {
this.pendingConversationsUuid.push(uuid);
String takePhotoUri = savedInstanceState.getString(STATE_PHOTO_URI);
@@ -2059,6 +2102,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.reInitRequiredOnStart = true;
pendingExtras.push(extras);
}
+ updateUnreadMessagesCount();
}
private void reInit(Conversation conversation) {
@@ -2164,6 +2208,20 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return true;
}
+ private void updateUnreadMessagesCount() {
+ lastMessageUuid = null;
+ hideUnreadMessagesCount();
+ }
+
+ private void hideUnreadMessagesCount() {
+ if (this.binding == null) {
+ return;
+ }
+ this.binding.scrollToBottomButton.setEnabled(false);
+ this.binding.scrollToBottomButton.setVisibility(View.GONE);
+ this.binding.unreadCountCustomView.setVisibility(View.GONE);
+ }
+
private void setSelection(int pos) {
this.binding.messagesView.setSelection(pos);
this.binding.messagesView.post(() -> this.binding.messagesView.setSelection(pos));
@@ -2173,8 +2231,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (this.binding == null) {
return false;
}
- final ListView listView = this.binding.messagesView;
- if (listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1) {
+ return scrolledToBottom(this.binding.messagesView);
+ }
+
+ private static boolean scrolledToBottom(AbsListView listView) {
+ if (listView.getLastVisiblePosition() == listView.getCount() - 1) {
final View lastChild = listView.getChildAt(listView.getChildCount() - 1);
return lastChild != null && lastChild.getBottom() <= listView.getHeight();
} else {
@@ -2362,6 +2423,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
conversation.populateWithMessages(ConversationFragment.this.messageList);
updateSnackBar(conversation);
updateStatusMessages();
+ if (conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid) != 0) {
+ binding.unreadCountCustomView.setVisibility(View.VISIBLE);
+ binding.unreadCountCustomView.setUnreadCount(conversation.getReceivedMessagesCountSinceUuid(lastMessageUuid));
+ }
this.messageListAdapter.notifyDataSetChanged();
updateChatMsgHint();
if (notifyConversationRead && activity != null) {
@@ -2886,7 +2951,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (lastHistoryMessage != null) {
int pos = getIndexOf(lastHistoryMessage.getUuid(), messageList);
- setScrollPosition(getScrollPosition(pos, getView()));
+ setScrollPosition(getScrollPosition(pos, getView()), null);
this.binding.messagesView.setSelection(pos);
}
return lastHistoryMessage;
@@ -2916,8 +2981,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
reInit(conversation);
ScrollState scrollState = pendingScrollState.pop();
+ String lastMessageUuid = pendingLastMessageUuid.pop();
if (scrollState != null) {
- setScrollPosition(scrollState);
+ setScrollPosition(scrollState, lastMessageUuid);
}
} else {
if (!activity.xmppConnectionService.isConversationStillOpen(conversation)) {
diff --git a/src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java b/src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java
new file mode 100644
index 000000000..6b4a6de0f
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/widget/UnreadCountCustomView.java
@@ -0,0 +1,77 @@
+package de.pixart.messenger.ui.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.support.v4.content.ContextCompat;
+import android.util.AttributeSet;
+import android.view.View;
+
+import de.pixart.messenger.R;
+
+public class UnreadCountCustomView extends View {
+
+ private int unreadCount;
+ private Paint paint, textPaint;
+ private int backgroundColor = 0xff326130;
+
+ public UnreadCountCustomView(Context context) {
+ super(context);
+ init();
+ }
+
+ public UnreadCountCustomView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initXMLAttrs(context, attrs);
+ init();
+ }
+
+ public UnreadCountCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initXMLAttrs(context, attrs);
+ init();
+ }
+
+ private void initXMLAttrs(Context context, AttributeSet attrs) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnreadCountCustomView);
+ //setBackgroundColor(a.getColor(a.getIndex(0), ContextCompat.getColor(context, R.color.accent)));
+ setBackgroundColor(ContextCompat.getColor(context, R.color.accent));
+ a.recycle();
+ }
+
+ void init() {
+ paint = new Paint();
+ paint.setColor(backgroundColor);
+ paint.setAntiAlias(true);
+ textPaint = new Paint();
+ textPaint.setColor(Color.WHITE);
+ textPaint.setTextAlign(Paint.Align.CENTER);
+ textPaint.setAntiAlias(true);
+ textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ float midx = canvas.getWidth() / 2.0f;
+ float midy = canvas.getHeight() / 2.0f;
+ float radius = Math.min(canvas.getWidth(), canvas.getHeight()) / 2.0f;
+ float textOffset = canvas.getWidth() / 6.0f;
+ textPaint.setTextSize(0.95f * radius);
+ canvas.drawCircle(midx, midy, radius * 0.94f, paint);
+ canvas.drawText(unreadCount > 999 ? "\u221E" : String.valueOf(unreadCount), midx, midy + textOffset, textPaint);
+
+ }
+
+ public void setUnreadCount(int unreadCount) {
+ this.unreadCount = unreadCount;
+ invalidate();
+ }
+
+ public void setBackgroundColor(int backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+}
diff --git a/src/main/res/drawable/ic_scroll_to_end_black.xml b/src/main/res/drawable/ic_scroll_to_end_black.xml
new file mode 100644
index 000000000..6771786c1
--- /dev/null
+++ b/src/main/res/drawable/ic_scroll_to_end_black.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="#8a000000"
+ android:pathData="M16.59,5.59L18,7L12,13L6,7L7.41,5.59L12,10.17L16.59,5.59M16.59,11.59L18,13L12,19L6,13L7.41,11.59L12,16.17L16.59,11.59Z" />
+</vector> \ No newline at end of file
diff --git a/src/main/res/drawable/ic_scroll_to_end_white.xml b/src/main/res/drawable/ic_scroll_to_end_white.xml
new file mode 100644
index 000000000..82ed9911f
--- /dev/null
+++ b/src/main/res/drawable/ic_scroll_to_end_white.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ ~
+ ~ Redistribution and use in source and binary forms, with or without modification,
+ ~ are permitted provided that the following conditions are met:
+ ~
+ ~ 1. Redistributions of source code must retain the above copyright notice, this
+ ~ list of conditions and the following disclaimer.
+ ~
+ ~ 2. Redistributions in binary form must reproduce the above copyright notice,
+ ~ this list of conditions and the following disclaimer in the documentation and/or
+ ~ other materials provided with the distribution.
+ ~
+ ~ 3. Neither the name of the copyright holder nor the names of its contributors
+ ~ may be used to endorse or promote products derived from this software without
+ ~ specific prior written permission.
+ ~
+ ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ~ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ ~ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ ~ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ ~ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ ~ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ~ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ~ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ ~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="#b2ffffff"
+ android:pathData="M16.59,5.59L18,7L12,13L6,7L7.41,5.59L12,10.17L16.59,5.59M16.59,11.59L18,13L12,19L6,13L7.41,11.59L12,16.17L16.59,11.59Z" />
+</vector> \ No newline at end of file
diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml
index 54c608f13..dcf825dc5 100644
--- a/src/main/res/layout/fragment_conversation.xml
+++ b/src/main/res/layout/fragment_conversation.xml
@@ -5,8 +5,8 @@
<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:clickable="false"
- android:background="?attr/color_background_secondary">
+ android:background="?attr/color_background_secondary"
+ android:clickable="false">
<ListView
android:id="@+id/messages_view"
@@ -23,6 +23,29 @@
android:transcriptMode="normal"
tools:listitem="@layout/message_sent"></ListView>
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/scroll_to_bottom_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/messages_view"
+ android:layout_alignParentEnd="true"
+ android:layout_margin="12dp"
+ android:alpha="0.85"
+ android:src="?attr/icon_scroll_down"
+ android:visibility="gone"
+ app:backgroundTint="?attr/color_background_primary"
+ app:fabSize="mini" />
+
+ <de.pixart.messenger.ui.widget.UnreadCountCustomView
+ android:id="@+id/unread_count_custom_view"
+ android:layout_width="?attr/IconSize"
+ android:layout_height="?attr/IconSize"
+ android:layout_alignTop="@+id/scroll_to_bottom_button"
+ android:layout_alignEnd="@+id/scroll_to_bottom_button"
+ android:elevation="8dp"
+ android:visibility="gone"
+ app:backgroundColor="?attr/unread_count" />
+
<RelativeLayout
android:id="@+id/input"
android:layout_width="match_parent"
@@ -100,7 +123,6 @@
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/textSendButton"
android:background="?attr/color_background_secondary"
- android:textColor="?attr/text_Color_Main"
android:ems="10"
android:imeOptions="flagNoExtractUi|actionSend"
android:inputType="textShortMessage|textMultiLine|textCapSentences"
@@ -111,7 +133,8 @@
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="12dp"
- android:singleLine="false">
+ android:singleLine="false"
+ android:textColor="?attr/text_Color_Main">
</de.pixart.messenger.ui.widget.EditMessage>
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index f9fb003f6..02461ec1a 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -52,6 +52,7 @@
<attr name="icon_import_export" format="reference" />
<attr name="icon_scan_qr_code" format="reference" />
<attr name="icon_enable_undecided_device" format="reference" />
+ <attr name="icon_scroll_down" format="reference"/>
<attr name="icon_notifications" format="reference" />
<attr name="icon_notifications_off" format="reference" />
@@ -92,4 +93,8 @@
<attr name="ic_settings_security" format="reference"/>
<attr name="ic_settings_expert" format="reference"/>
<attr name="ic_settings_about" format="reference"/>
+
+ <declare-styleable name="UnreadCountCustomView">
+ <attr name="backgroundColor" format="reference|color" />
+ </declare-styleable>
</resources> \ No newline at end of file
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index e757b7817..85c18e84e 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -61,6 +61,7 @@
<item type="reference" name="icon_import_export">@drawable/ic_import_export_white_24dp</item>
<item type="reference" name="icon_share">@drawable/ic_share_white_24dp</item>
<item type="reference" name="icon_scan_qr_code">@drawable/ic_barcode_scan_white_24dp</item>
+ <item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_black</item>
<!-- settings-->
<item type="reference" name="ic_settings_ui">@drawable/ic_image_black_24dp</item>
@@ -173,7 +174,8 @@
<item type="reference" name="icon_settings">@drawable/ic_settings_white_24dp</item>
<item type="reference" name="icon_import_export">@drawable/ic_import_export_white_24dp</item>
<item type="reference" name="icon_share">@drawable/ic_share_white_24dp</item>
- <item name="icon_scan_qr_code">@drawable/ic_barcode_scan_white_24dp</item>
+ <item type="reference" name="icon_scan_qr_code">@drawable/ic_barcode_scan_white_24dp</item>
+ <item type="reference" name="icon_scroll_down">@drawable/ic_scroll_to_end_white</item>
<item type="reference" name="icon_notifications">@drawable/ic_notifications_white_24dp</item>
<item type="reference" name="icon_notifications_off">@drawable/ic_notifications_off_white_24dp</item>