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\'