diff --git a/FAQ.md b/FAQ.md
index c802ad9628..82fc28a6cc 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -1220,7 +1220,8 @@ By selecting a zero snooze duration you can cancel snoozing.
**(69) Can you add auto scroll up on new message?**
The message list is automatically scrolled up when navigating from a new message notification or after a manual refresh.
-Always automatically scrolling up on arrival of new messages would interfere with your own navigation and scrolling.
+Always automatically scrolling up on arrival of new messages would interfere with your own scrolling,
+but if you like you can enable this in the advanced options.
diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java
index 39f2bb61a7..dceb1bda3a 100644
--- a/app/src/main/java/eu/faircode/email/FragmentMessages.java
+++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java
@@ -136,6 +136,7 @@ public class FragmentMessages extends FragmentBase {
private boolean date;
private boolean threading;
private boolean pull;
+ private boolean autoscroll;
private boolean actionbar;
private boolean autoexpand;
private boolean autoclose;
@@ -148,6 +149,8 @@ public class FragmentMessages extends FragmentBase {
private String searching = null;
private boolean refresh = false;
private boolean manual = false;
+ private Integer lastUnseen = null;
+
private AdapterMessage adapter;
private AdapterMessage.ViewType viewType;
@@ -233,6 +236,7 @@ public class FragmentMessages extends FragmentBase {
else
pull = false;
+ autoscroll = prefs.getBoolean("autoscroll", false);
date = prefs.getBoolean("date", true);
threading = prefs.getBoolean("threading", true);
actionbar = prefs.getBoolean("actionbar", true);
@@ -1715,45 +1719,8 @@ public class FragmentMessages extends FragmentBase {
public void onChanged(List folders) {
if (folders == null)
folders = new ArrayList<>();
- Log.i("Folder state updated count=" + folders.size());
-
- int unseen = 0;
- boolean sync = false;
- boolean errors = false;
- for (TupleFolderEx folder : folders) {
- unseen += folder.unseen;
- if (folder.synchronize)
- sync = true;
- if (folder.error != null)
- errors = true;
- }
-
- if (unseen > 0)
- setSubtitle(getString(R.string.title_name_count,
- getString(R.string.title_folder_unified),
- nf.format(unseen)));
- else
- setSubtitle(getString(R.string.title_folder_unified));
-
- boolean refreshing = false;
- for (TupleFolderEx folder : folders)
- if (folder.sync_state != null &&
- (folder.account == null || "connected".equals(folder.accountState))) {
- refreshing = true;
- break;
- }
- if (!refreshing && manual) {
- manual = false;
- rvMessage.scrollToPosition(0);
- }
-
- if (errors && !refreshing && swipeRefresh.isRefreshing())
- Snackbar.make(view, R.string.title_sync_errors, Snackbar.LENGTH_LONG).show();
-
- refresh = sync;
- swipeRefresh.setEnabled(pull && refresh);
- swipeRefresh.setRefreshing(refreshing);
+ updateState(folders);
}
});
db.message().liveHidden(null).observe(getViewLifecycleOwner(), new Observer>() {
@@ -1770,37 +1737,17 @@ public class FragmentMessages extends FragmentBase {
db.folder().liveFolderEx(folder).observe(getViewLifecycleOwner(), new Observer() {
@Override
public void onChanged(@Nullable TupleFolderEx folder) {
- Log.i("Folder state updated");
- if (folder == null)
- setSubtitle(null);
- else {
- if (folder.unseen > 0)
- setSubtitle(getString(R.string.title_name_count,
- folder.getDisplayName(getContext()),
- nf.format(folder.unseen)));
- else
- setSubtitle(folder.getDisplayName(getContext()));
+ List folders = new ArrayList<>();
+ if (folder != null)
+ folders.add(folder);
- boolean outbox = EntityFolder.OUTBOX.equals(folder.type);
- if (FragmentMessages.this.outbox != outbox) {
- FragmentMessages.this.outbox = outbox;
- getActivity().invalidateOptionsMenu();
- }
- }
+ updateState(folders);
- boolean refreshing = (folder != null && folder.sync_state != null &&
- (folder.account == null || "connected".equals(folder.accountState)));
- if (!refreshing && manual) {
- manual = false;
- rvMessage.scrollToPosition(0);
+ boolean outbox = EntityFolder.OUTBOX.equals(folder.type);
+ if (FragmentMessages.this.outbox != outbox) {
+ FragmentMessages.this.outbox = outbox;
+ getActivity().invalidateOptionsMenu();
}
-
- if (folder != null && folder.error != null && !refreshing && swipeRefresh.isRefreshing())
- Snackbar.make(view, folder.error, Snackbar.LENGTH_LONG).show();
-
- refresh = (folder != null);
- swipeRefresh.setEnabled(pull && refresh);
- swipeRefresh.setRefreshing(refreshing);
}
});
db.message().liveHidden(folder).observe(getViewLifecycleOwner(), new Observer>() {
@@ -2213,6 +2160,67 @@ public class FragmentMessages extends FragmentBase {
}.execute(this, args, "messages:all");
}
+ private void updateState(List folders) {
+ Log.i("Folder state updated count=" + folders.size());
+
+ // Get state
+ int unseen = 0;
+ boolean sync = false;
+ boolean errors = false;
+ boolean refreshing = false;
+ for (TupleFolderEx folder : folders) {
+ unseen += folder.unseen;
+ if (folder.synchronize)
+ sync = true;
+ if (folder.error != null)
+ errors = true;
+ if (folder.sync_state != null &&
+ (folder.account == null || "connected".equals(folder.accountState))) {
+ refreshing = true;
+ break;
+ }
+ }
+
+ // Get name
+ String name;
+ if (viewType == AdapterMessage.ViewType.UNIFIED)
+ name = getString(R.string.title_folder_unified);
+ else
+ name = (folders.size() > 0 ? folders.get(0).getDisplayName(getContext()) : "");
+
+ // Show name/unread
+ if (unseen == 0)
+ setSubtitle(name);
+ else
+ setSubtitle(getString(R.string.title_name_count, name, nf.format(unseen)));
+
+ // Auto scroll
+ if (lastUnseen == null || lastUnseen != unseen) {
+ if ((!refreshing && manual) ||
+ (autoscroll && lastUnseen != null && lastUnseen < unseen))
+ // Delay to let list update
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ rvMessage.scrollToPosition(0);
+ }
+ }, 3000);
+ manual = false;
+ lastUnseen = unseen;
+ }
+
+ // Show errors
+ if (errors && !refreshing && swipeRefresh.isRefreshing())
+ if (folders.size() == 1)
+ Snackbar.make(view, folders.get(0).error, Snackbar.LENGTH_LONG).show();
+ else
+ Snackbar.make(view, R.string.title_sync_errors, Snackbar.LENGTH_LONG).show();
+
+ refresh = sync;
+ swipeRefresh.setEnabled(pull && refresh);
+ swipeRefresh.setRefreshing(refreshing);
+ }
+
private void loadMessages() {
if (viewType == AdapterMessage.ViewType.THREAD && autonext) {
ViewModelMessages model = ViewModelProviders.of(getActivity()).get(ViewModelMessages.class);
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptions.java b/app/src/main/java/eu/faircode/email/FragmentOptions.java
index 57ac10b3fe..e7cdd13365 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptions.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptions.java
@@ -91,6 +91,7 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O
private SwitchCompat swActionbar;
private SwitchCompat swPull;
+ private SwitchCompat swAutoScroll;
private SwitchCompat swSwipeNav;
private SwitchCompat swAutoExpand;
private SwitchCompat swAutoClose;
@@ -125,7 +126,7 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O
static String[] OPTIONS_RESTART = new String[]{
"startup", "date", "threading", "avatars", "identicons", "circular", "name_email", "subject_italic", "flags", "preview",
"addresses", "monospaced", "autohtml", "autoimages", "actionbar",
- "pull", "swipenav", "autoexpand", "autoclose", "autonext",
+ "pull", "autoscroll", "swipenav", "autoexpand", "autoclose", "autonext",
"subscriptions",
"authentication", "debug"
};
@@ -135,7 +136,7 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O
"metered", "download", "roaming",
"startup", "date", "threading", "avatars", "identicons", "circular", "name_email", "subject_italic", "flags", "preview",
"addresses", "monospaced", "autohtml", "autoimages", "actionbar",
- "pull", "swipenav", "autoexpand", "autoclose", "autonext", "collapse", "autoread", "automove",
+ "pull", "autoscroll", "swipenav", "autoexpand", "autoclose", "autonext", "collapse", "autoread", "automove",
"autoresize", "resize", "prefix_once", "autosend",
"badge", "subscriptions", "notify_preview", "search_local", "light", "sound",
"authentication", "paranoid", "english", "updates", "debug",
@@ -181,6 +182,7 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O
swActionbar = view.findViewById(R.id.swActionbar);
swPull = view.findViewById(R.id.swPull);
+ swAutoScroll = view.findViewById(R.id.swAutoScroll);
swSwipeNav = view.findViewById(R.id.swSwipeNav);
swAutoExpand = view.findViewById(R.id.swAutoExpand);
swAutoClose = view.findViewById(R.id.swAutoClose);
@@ -443,6 +445,13 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O
}
});
+ swAutoScroll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+ prefs.edit().putBoolean("autoscroll", checked).apply();
+ }
+ });
+
swSwipeNav.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@@ -753,6 +762,7 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O
swActionbar.setChecked(prefs.getBoolean("actionbar", true));
swPull.setChecked(prefs.getBoolean("pull", true));
+ swAutoScroll.setChecked(prefs.getBoolean("autoscroll", false));
swSwipeNav.setChecked(prefs.getBoolean("swipenav", true));
swAutoExpand.setChecked(prefs.getBoolean("autoexpand", true));
swAutoClose.setChecked(prefs.getBoolean("autoclose", true));
diff --git a/app/src/main/res/layout/fragment_options.xml b/app/src/main/res/layout/fragment_options.xml
index e952cb347d..127b5d1208 100644
--- a/app/src/main/res/layout/fragment_options.xml
+++ b/app/src/main/res/layout/fragment_options.xml
@@ -577,6 +577,18 @@
app:layout_constraintTop_toBottomOf="@id/vSeparatorBehavior"
app:switchPadding="12dp" />
+
+
Conversation action bar
Pull down to refresh
+ Scroll to top on receiving new messages
Swipe left/right to go to next/previous conversation
Automatically expand messages
Collapse messages in conversations on \'back\'