aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2018-03-31 15:03:40 +0200
committerChristian Schneppe <christian@pix-art.de>2018-03-31 15:03:40 +0200
commit661aff6a1fa019734e92ecde67e0fdc1ad16cdb1 (patch)
tree352c370c93ed862ac026a62c6d3ab67f44af01f4 /src
parente4523b6d7b6aed25e896b59f23b31d2a6b858744 (diff)
save scroll state across rotations
Diffstat (limited to '')
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationActivity.java2
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java158
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/ScrollState.java71
3 files changed, 168 insertions, 63 deletions
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
index 82f3f893d..0f6994b21 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
@@ -745,6 +745,8 @@ public class ConversationActivity extends XmppActivity implements OnConversation
public void onConversationRead(Conversation conversation) {
if (!mActivityPaused && pendingViewIntent.peek() == null) {
xmppConnectionService.sendReadMarker(conversation);
+ } else {
+ Log.d(Config.LOGTAG, "ignoring read callback. mActivityPaused=" + Boolean.toString(mActivityPaused));
}
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index 3ea9bfe5b..f0e715d1f 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -30,7 +30,6 @@ import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
-import android.util.Pair;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
@@ -101,6 +100,7 @@ import de.pixart.messenger.ui.util.AttachmentTool;
import de.pixart.messenger.ui.util.ConversationMenuConfigurator;
import de.pixart.messenger.ui.util.PendingItem;
import de.pixart.messenger.ui.util.PresenceSelector;
+import de.pixart.messenger.ui.util.ScrollState;
import de.pixart.messenger.ui.util.SendButtonAction;
import de.pixart.messenger.ui.util.SendButtonTool;
import de.pixart.messenger.ui.widget.EditMessage;
@@ -148,6 +148,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
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<>();
protected MessageAdapter messageListAdapter;
private Conversation conversation;
public FragmentConversationBinding binding;
@@ -463,8 +464,41 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
private String incomplete;
private int lastCompletionCursor;
private boolean firstWord = false;
-
private Message mPendingDownloadableMessage;
+
+ public static void downloadFile(Activity activity, Message message) {
+ Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.main_fragment);
+ if (fragment != null && fragment instanceof ConversationFragment) {
+ ((ConversationFragment) fragment).startDownloadable(message);
+ return;
+ }
+ fragment = activity.getFragmentManager().findFragmentById(R.id.secondary_fragment);
+ if (fragment != null && fragment instanceof ConversationFragment) {
+ ((ConversationFragment) fragment).startDownloadable(message);
+ }
+ }
+
+ public static Conversation getConversation(Activity activity) {
+ return getConversation(activity, R.id.secondary_fragment);
+ }
+
+ private static Conversation getConversation(Activity activity, @IdRes int res) {
+ final Fragment fragment = activity.getFragmentManager().findFragmentById(res);
+ if (fragment != null && fragment instanceof ConversationFragment) {
+ return ((ConversationFragment) fragment).getConversation();
+ } else {
+ return null;
+ }
+ }
+
+ public static Conversation getConversationReliable(Activity activity) {
+ final Conversation conversation = getConversation(activity, R.id.secondary_fragment);
+ if (conversation != null) {
+ return conversation;
+ }
+ return getConversation(activity, R.id.main_fragment);
+ }
+
private TextWatcher mSearchTextWatcher = new TextWatcher() {
@Override
@@ -533,24 +567,39 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return -1;
}
- public Pair<Integer, Integer> getScrollPosition() {
- if (this.binding.messagesView.getCount() == 0 ||
- this.binding.messagesView.getLastVisiblePosition() == this.binding.messagesView.getCount() - 1) {
+ private ScrollState getScrollPosition(int pos, View view) {
+ final ListView listView = this.binding.messagesView;
+ if (listView.getCount() == 0 || listView.getLastVisiblePosition() == listView.getCount() - 1) {
return null;
} else {
- final int pos = binding.messagesView.getFirstVisiblePosition();
- final View view = binding.messagesView.getChildAt(0);
+ //final int pos = listView.getFirstVisiblePosition();
+ //final View view = listView.getChildAt(0);
if (view == null) {
return null;
} else {
- return new Pair<>(pos, view.getTop());
+ return new ScrollState(pos, view.getTop());
}
}
}
- public void setScrollPosition(Pair<Integer, Integer> scrollPosition) {
+ private ScrollState getScrollPosition() {
+ final ListView listView = this.binding.messagesView;
+ if (listView.getCount() == 0 || listView.getLastVisiblePosition() == listView.getCount() - 1) {
+ return null;
+ } else {
+ final int pos = listView.getFirstVisiblePosition();
+ final View view = listView.getChildAt(0);
+ if (view == null) {
+ return null;
+ } else {
+ return new ScrollState(pos, view.getTop());
+ }
+ }
+ }
+
+ private void setScrollPosition(ScrollState scrollPosition) {
if (scrollPosition != null) {
- this.binding.messagesView.setSelectionFromTop(scrollPosition.first, scrollPosition.second);
+ this.binding.messagesView.setSelectionFromTop(scrollPosition.position, scrollPosition.offset);
}
}
@@ -1634,6 +1683,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
getActivity().invalidateOptionsMenu();
});
super.onResume();
+ if (activity != null && this.conversation != null) {
+ activity.onConversationRead(this.conversation);
+ }
}
private void showErrorMessage(final Message message) {
@@ -1738,18 +1790,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
- public static void downloadFile(Activity activity, Message message) {
- Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.main_fragment);
- if (fragment != null && fragment instanceof ConversationFragment) {
- ((ConversationFragment) fragment).startDownloadable(message);
- return;
- }
- fragment = activity.getFragmentManager().findFragmentById(R.id.secondary_fragment);
- if (fragment != null && fragment instanceof ConversationFragment) {
- ((ConversationFragment) fragment).startDownloadable(message);
- }
- }
-
private void cancelTransmission(Message message) {
Transferable transferable = message.getTransferable();
if (transferable != null) {
@@ -1820,10 +1860,14 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
super.onSaveInstanceState(outState);
if (conversation != null) {
outState.putString(STATE_CONVERSATION_UUID, conversation.getUuid());
- Uri uri = pendingTakePhotoUri.pop();
+ final Uri uri = pendingTakePhotoUri.pop();
if (uri != null) {
outState.putString(STATE_PHOTO_URI, uri.toString());
}
+ final ScrollState scrollState = getScrollPosition();
+ if (scrollState != null) {
+ outState.putParcelable(STATE_SCROLL_POSITION, scrollState);
+ }
}
}
@@ -1840,6 +1884,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (takePhotoUri != null) {
pendingTakePhotoUri.push(Uri.parse(takePhotoUri));
}
+ pendingScrollState.push(savedInstanceState.getParcelable(STATE_SCROLL_POSITION));
}
}
@@ -1917,14 +1962,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (conversation == null) {
return false;
}
+ final boolean hasChanged = this.conversation != null && this.conversation != conversation;
this.conversation = conversation;
//once we set the conversation all is good and it will automatically do the right thing in onStart()
if (this.activity == null || this.binding == null) {
return false;
}
- Log.d(Config.LOGTAG, "reInit(restore=" + Boolean.toString(restore) + ")");
+ Log.d(Config.LOGTAG, "reInit(restore=" + Boolean.toString(restore) + ", hasChanged=" + Boolean.toString(hasChanged) + ")");
setupIme();
- if (!restore) {
+ if (!restore && hasChanged) {
this.conversation.trim();
}
@@ -1934,11 +1980,25 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.textinput.append(this.conversation.getNextMessage());
this.binding.textinput.setKeyboardListener(this);
messageListAdapter.updatePreferences();
- this.binding.messagesView.setAdapter(messageListAdapter);
+ if (!restore && hasChanged) {
+ this.binding.messagesView.setAdapter(messageListAdapter);
+ }
refresh(false);
- refresh();
this.conversation.messagesLoaded.set(true);
- final boolean isAtBottom;
+ if (!restore && hasChanged) {
+ synchronized (this.messageList) {
+ final Message first = conversation.getFirstUnreadMessage();
+ final int bottom = Math.max(0, this.messageList.size() - 1);
+ final int pos;
+ if (first == null) {
+ pos = bottom;
+ } else {
+ int i = getIndexOf(first.getUuid(), this.messageList);
+ pos = i < 0 ? bottom : i;
+ }
+ this.binding.messagesView.setSelection(pos);
+ }
+ }
this.binding.messagesView.setOnTouchListener(new OnSwipeTouchListener(getContext()) {
@Override
public void onSwipeRight() {
@@ -1946,20 +2006,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
activity.onBackPressed();
}
});
- synchronized (this.messageList) {
- final Message first = conversation.getFirstUnreadMessage();
- final int bottom = Math.max(0, this.messageList.size() - 1);
- final int pos;
- if (first == null) {
- pos = bottom;
- } else {
- int i = getIndexOf(first.getUuid(), this.messageList);
- pos = i < 0 ? bottom : i;
- }
- this.binding.messagesView.setSelection(pos);
- isAtBottom = pos == bottom;
- }
-
activity.onConversationRead(this.conversation);
//TODO if we only do this when this fragment is running on main it won't *bing* in tablet layout which might be unnecessary since we can *see* it
activity.xmppConnectionService.getNotificationService().setOpenConversation(this.conversation);
@@ -2649,7 +2695,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (lastHistoryMessage != null) {
int pos = getIndexOf(lastHistoryMessage.getUuid(), messageList);
- setScrollPosition(new Pair<>(pos, pos));
+ setScrollPosition(getScrollPosition(pos, getView()));
this.binding.messagesView.setSelection(pos);
}
return lastHistoryMessage;
@@ -2678,6 +2724,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return;
}
reInit(conversation, true);
+ ScrollState scrollState = pendingScrollState.pop();
+ if (scrollState != null) {
+ setScrollPosition(scrollState);
+ }
}
ActivityResult activityResult = postponedActivityResult.pop();
if (activityResult != null) {
@@ -2689,28 +2739,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (postponedActivityResult.pop() != null) {
Log.d(Config.LOGTAG, "cleared pending intent with unhandled result left");
}
+ pendingScrollState.pop();
+ pendingTakePhotoUri.pop();
}
- public static Conversation getConversation(Activity activity) {
- return getConversation(activity, R.id.secondary_fragment);
- }
-
- private static Conversation getConversation(Activity activity, @IdRes int res) {
- final Fragment fragment = activity.getFragmentManager().findFragmentById(res);
- if (fragment != null && fragment instanceof ConversationFragment) {
- return ((ConversationFragment) fragment).getConversation();
- } else {
- return null;
- }
- }
-
- public static Conversation getConversationReliable(Activity activity) {
- final Conversation conversation = getConversation(activity, R.id.secondary_fragment);
- if (conversation != null) {
- return conversation;
- }
- return getConversation(activity, R.id.main_fragment);
- }
public Conversation getConversation() {
return conversation;
diff --git a/src/main/java/de/pixart/messenger/ui/util/ScrollState.java b/src/main/java/de/pixart/messenger/ui/util/ScrollState.java
new file mode 100644
index 000000000..3a0e87900
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/util/ScrollState.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package de.pixart.messenger.ui.util;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ScrollState implements Parcelable {
+
+ public static final Creator<ScrollState> CREATOR = new Creator<ScrollState>() {
+ @Override
+ public ScrollState createFromParcel(Parcel in) {
+ return new ScrollState(in);
+ }
+
+ @Override
+ public ScrollState[] newArray(int size) {
+ return new ScrollState[size];
+ }
+ };
+ public final int position;
+ public final int offset;
+
+ private ScrollState(Parcel in) {
+ position = in.readInt();
+ offset = in.readInt();
+ }
+
+ public ScrollState(int position, int offset) {
+ this.position = position;
+ this.offset = offset;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(position);
+ dest.writeInt(offset);
+ }
+} \ No newline at end of file