Browse Source

Previous/next navigation

main
M66B 6 years ago
parent
commit
655e25258f
6 changed files with 132 additions and 20 deletions
  1. +0
    -2
      FAQ.md
  2. +27
    -0
      app/src/main/java/eu/faircode/email/ActivityView.java
  3. +59
    -18
      app/src/main/java/eu/faircode/email/FragmentMessages.java
  4. +10
    -0
      app/src/main/res/drawable/baseline_skip_next_24.xml
  5. +10
    -0
      app/src/main/res/drawable/baseline_skip_previous_24.xml
  6. +26
    -0
      app/src/main/res/layout/fragment_messages.xml

+ 0
- 2
FAQ.md View File

@ -130,8 +130,6 @@ See [here](https://support.microsoft.com/en-us/help/12409/microsoft-account-app-
* More themes: the goal is to keep the app as simple as possible, so this will not be added. * More themes: the goal is to keep the app as simple as possible, so this will not be added.
* Encryption: there is too little interest in sending/receiving encrypted messages to justify putting effort into this. * Encryption: there is too little interest in sending/receiving encrypted messages to justify putting effort into this.
* Multiple select: swiping is easier and doesn't have the risk of accidental touches, so multiple select would not add anything. * Multiple select: swiping is easier and doesn't have the risk of accidental touches, so multiple select would not add anything.
* Swipe left/right for previous/next message: this would be confusing since sometimes a message and sometimes a conversation would be shown.
* Open message from notification: this would be confusing since sometimes a message and sometimes a conversation would be opened.
* Preview message text: this is not always possible because the message text is initially not downloaded for large messages and besides that the subject is supposed to tell what the message is about. * Preview message text: this is not always possible because the message text is initially not downloaded for large messages and besides that the subject is supposed to tell what the message is about.
* Filter rules: filter rules should be executed on the server because a battery powered device with possibly an unstable internet connection is not suitable for executing filter rules. * Filter rules: filter rules should be executed on the server because a battery powered device with possibly an unstable internet connection is not suitable for executing filter rules.
* Widget: FairEmail can be started from a shortcut and new messages are reported as status bar notifications, so I don't see what a widget would add. * Widget: FairEmail can be started from a shortcut and new messages are reported as status bar notifications, so I don't see what a widget would add.


+ 27
- 0
app/src/main/java/eu/faircode/email/ActivityView.java View File

@ -75,6 +75,7 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.paging.PagedList;
public class ActivityView extends ActivityBilling implements FragmentManager.OnBackStackChangedListener { public class ActivityView extends ActivityBilling implements FragmentManager.OnBackStackChangedListener {
private View view; private View view;
@ -83,6 +84,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
private ActionBarDrawerToggle drawerToggle; private ActionBarDrawerToggle drawerToggle;
private long attachment = -1; private long attachment = -1;
private PagedList<TupleMessageEx> messages = null;
private static final int ATTACHMENT_BUFFER_SIZE = 8192; // bytes private static final int ATTACHMENT_BUFFER_SIZE = 8192; // bytes
@ -883,4 +885,29 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
}.load(this, args); }.load(this, args);
} }
} }
void setMessages(PagedList<TupleMessageEx> messages) {
this.messages = messages;
}
public String[] getPrevNext(String thread) {
boolean found = false;
TupleMessageEx prev = null;
TupleMessageEx next = null;
for (int i = 0; i < messages.size(); i++) {
TupleMessageEx item = messages.get(i);
if (item == null)
continue;
if (found) {
next = item;
messages.loadAround(i);
break;
}
if (thread.equals(item.thread))
found = true;
else
prev = item;
}
return new String[]{prev == null ? null : prev.thread, next == null ? null : next.thread};
}
} }

+ 59
- 18
app/src/main/java/eu/faircode/email/FragmentMessages.java View File

@ -52,10 +52,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.constraintlayout.widget.Group; import androidx.constraintlayout.widget.Group;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.paging.LivePagedListBuilder; import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList; import androidx.paging.PagedList;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
@ -75,6 +77,8 @@ public class FragmentMessages extends FragmentEx {
private Group grpHintActions; private Group grpHintActions;
private Group grpReady; private Group grpReady;
private FloatingActionButton fab; private FloatingActionButton fab;
private FloatingActionButton fabPrev;
private FloatingActionButton fabNext;
private long folder = -1; private long folder = -1;
private long account = -1; private long account = -1;
@ -107,12 +111,21 @@ public class FragmentMessages extends FragmentEx {
// Get arguments // Get arguments
Bundle args = getArguments(); Bundle args = getArguments();
if (args != null) {
account = args.getLong("account", -1);
folder = args.getLong("folder", -1);
thread = args.getString("thread");
search = args.getString("search");
}
account = args.getLong("account", -1);
folder = args.getLong("folder", -1);
thread = args.getString("thread");
search = args.getString("search");
if (TextUtils.isEmpty(search))
if (thread == null)
if (folder < 0)
viewType = AdapterMessage.ViewType.UNIFIED;
else
viewType = AdapterMessage.ViewType.FOLDER;
else
viewType = AdapterMessage.ViewType.THREAD;
else
viewType = AdapterMessage.ViewType.SEARCH;
} }
@Override @Override
@ -134,6 +147,8 @@ public class FragmentMessages extends FragmentEx {
grpHintActions = view.findViewById(R.id.grpHintActions); grpHintActions = view.findViewById(R.id.grpHintActions);
grpReady = view.findViewById(R.id.grpReady); grpReady = view.findViewById(R.id.grpReady);
fab = view.findViewById(R.id.fab); fab = view.findViewById(R.id.fab);
fabPrev = view.findViewById(R.id.fabPrev);
fabNext = view.findViewById(R.id.fabNext);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
@ -168,17 +183,6 @@ public class FragmentMessages extends FragmentEx {
LinearLayoutManager llm = new LinearLayoutManager(getContext()); LinearLayoutManager llm = new LinearLayoutManager(getContext());
rvMessage.setLayoutManager(llm); rvMessage.setLayoutManager(llm);
if (TextUtils.isEmpty(search))
if (thread == null)
if (folder < 0)
viewType = AdapterMessage.ViewType.UNIFIED;
else
viewType = AdapterMessage.ViewType.FOLDER;
else
viewType = AdapterMessage.ViewType.THREAD;
else
viewType = AdapterMessage.ViewType.SEARCH;
adapter = new AdapterMessage(getContext(), getViewLifecycleOwner(), viewType, new AdapterMessage.IProperties() { adapter = new AdapterMessage(getContext(), getViewLifecycleOwner(), viewType, new AdapterMessage.IProperties() {
@Override @Override
public void setExpanded(long id, boolean expand) { public void setExpanded(long id, boolean expand) {
@ -467,11 +471,30 @@ public class FragmentMessages extends FragmentEx {
} }
}); });
View.OnClickListener navigate = new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager().popBackStack("thread", FragmentManager.POP_BACK_STACK_INCLUSIVE);
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_THREAD)
.putExtra("account", account)
.putExtra("thread", (String) v.getTag()));
}
};
fabPrev.setOnClickListener(navigate);
fabNext.setOnClickListener(navigate);
// Initialize // Initialize
tvNoEmail.setVisibility(View.GONE); tvNoEmail.setVisibility(View.GONE);
grpReady.setVisibility(View.GONE); grpReady.setVisibility(View.GONE);
pbWait.setVisibility(View.VISIBLE); pbWait.setVisibility(View.VISIBLE);
fab.hide(); fab.hide();
fabPrev.hide();
fabNext.hide();
return view; return view;
} }
@ -561,7 +584,22 @@ public class FragmentMessages extends FragmentEx {
loadMessages(); loadMessages();
// Compose FAB // Compose FAB
if (viewType != AdapterMessage.ViewType.THREAD) {
if (viewType == AdapterMessage.ViewType.THREAD) {
String[] pn = ((ActivityView) getActivity()).getPrevNext(thread);
fabPrev.setTag(pn[0]);
fabNext.setTag(pn[1]);
if (pn[0] == null)
fabPrev.hide();
else
fabPrev.show();
if (pn[1] == null)
fabNext.hide();
else
fabNext.show();
} else {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("account", account); args.putLong("account", account);
@ -816,6 +854,9 @@ public class FragmentMessages extends FragmentEx {
return; return;
} }
if (viewType != AdapterMessage.ViewType.THREAD)
((ActivityView) getActivity()).setMessages(messages);
if (viewType == AdapterMessage.ViewType.THREAD && autoExpand) { if (viewType == AdapterMessage.ViewType.THREAD && autoExpand) {
autoExpand = false; autoExpand = false;


+ 10
- 0
app/src/main/res/drawable/baseline_skip_next_24.xml View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
</vector>

+ 10
- 0
app/src/main/res/drawable/baseline_skip_previous_24.xml View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M6,6h2v12L6,18zM9.5,12l8.5,6L18,6z"/>
</vector>

+ 26
- 0
app/src/main/res/layout/fragment_messages.xml View File

@ -162,4 +162,30 @@
app:backgroundTint="?attr/colorAccent" app:backgroundTint="?attr/colorAccent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabPrev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:layout_margin="16dp"
android:src="@drawable/baseline_skip_previous_24"
android:tint="@color/colorActionForeground"
app:backgroundTint="?attr/colorAccent"
app:fabSize="mini"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabNext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_margin="16dp"
android:src="@drawable/baseline_skip_next_24"
android:tint="@color/colorActionForeground"
app:backgroundTint="?attr/colorAccent"
app:fabSize="mini"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

Loading…
Cancel
Save