diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index 76e899b7f1..bbee247fe5 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -65,6 +65,7 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack static final int LOADER_IDENTITY_PUT = 2; static final int LOADER_FOLDER_PUT = 3; static final int LOADER_MESSAGES_INIT = 4; + static final int LOADER_MESSAGE_INIT = 5; static final int REQUEST_VIEW = 1; diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index fdb0ec50ab..f552578152 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -104,7 +104,6 @@ public class AdapterMessage extends RecyclerView.Adapter> liveAttachments(long message); + List getAttachments(long message); @Query("SELECT * FROM attachment WHERE message = :message AND sequence = :sequence") EntityAttachment getAttachment(long message, int sequence); diff --git a/app/src/main/java/eu/faircode/email/DaoFolder.java b/app/src/main/java/eu/faircode/email/DaoFolder.java index 801789d775..823c43e7d9 100644 --- a/app/src/main/java/eu/faircode/email/DaoFolder.java +++ b/app/src/main/java/eu/faircode/email/DaoFolder.java @@ -61,24 +61,20 @@ public interface DaoFolder { EntityFolder getFolder(Long id); @Query("SELECT * FROM folder WHERE account = :account AND name = :name") - EntityFolder getFolder(Long account, String name); + EntityFolder getFolderByName(Long account, String name); @Query("SELECT folder.* FROM folder" + - " JOIN account ON account.id = folder.account" + - " WHERE account.`primary` AND type = '" + EntityFolder.TYPE_DRAFTS + "' ") - EntityFolder getPrimaryDraftFolder(); - - @Query("SELECT folder.* FROM folder" + - " WHERE account = :account AND type = '" + EntityFolder.TYPE_ARCHIVE + "' ") - EntityFolder getArchiveFolder(long account); - - @Query("SELECT folder.* FROM folder" + - " WHERE account = :account AND type = '" + EntityFolder.TYPE_JUNK + "' ") - EntityFolder getSpamFolder(long account); + " WHERE account = :account AND type = :type") + EntityFolder getFolderByType(long account, String type); @Query("SELECT * FROM folder WHERE type = '" + EntityFolder.TYPE_OUTBOX + "'") EntityFolder getOutbox(); + @Query("SELECT folder.* FROM folder" + + " JOIN account ON account.id = folder.account" + + " WHERE account.`primary` AND type = '" + EntityFolder.TYPE_DRAFTS + "' ") + EntityFolder getPrimaryDraftFolder(); + @Insert(onConflict = OnConflictStrategy.REPLACE) long insertFolder(EntityFolder folder); diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index b2ff92c6d0..a2101764cf 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -218,14 +218,14 @@ public class FragmentCompose extends Fragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_cc, menu); + inflater.inflate(R.menu.menu_address, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_cc: + case R.id.menu_address: onMenuCc(); return true; default: diff --git a/app/src/main/java/eu/faircode/email/FragmentMessage.java b/app/src/main/java/eu/faircode/email/FragmentMessage.java index 2718da45ef..779ac34a39 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessage.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessage.java @@ -19,8 +19,8 @@ package eu.faircode.email; Copyright 2018 by Marcel Bokhorst (M66B) */ -import android.arch.lifecycle.LiveData; import android.arch.lifecycle.Observer; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Typeface; @@ -31,6 +31,9 @@ import android.support.constraint.Group; import android.support.design.widget.BottomNavigationView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.AsyncTaskLoader; +import android.support.v4.content.Loader; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; @@ -77,7 +80,6 @@ public class FragmentMessage extends Fragment { private Group grpReady; private AdapterAttachment adapter; - private LiveData liveFolder; private ExecutorService executor = Executors.newCachedThreadPool(); private DateFormat df = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); @@ -88,9 +90,7 @@ public class FragmentMessage extends Fragment { View view = inflater.inflate(R.layout.fragment_message, container, false); // Get arguments - Bundle args = getArguments(); - final long folder = args.getLong("folder"); - final long id = args.getLong("id"); + final long id = getArguments().getLong("id"); // Get controls tvFrom = view.findViewById(R.id.tvFrom); @@ -195,6 +195,7 @@ public class FragmentMessage extends Fragment { // Initialize grpAddress.setVisibility(View.GONE); grpAttachments.setVisibility(View.GONE); + bottom_navigation.setVisibility(View.GONE); grpReady.setVisibility(View.GONE); pbWait.setVisibility(View.VISIBLE); @@ -207,16 +208,10 @@ public class FragmentMessage extends Fragment { final DB db = DB.getInstance(getContext()); - // Observe folder - liveFolder = db.folder().liveFolderEx(folder); - // Observe message db.message().liveMessage(id).observe(this, new Observer() { @Override public void onChanged(@Nullable TupleMessageEx message) { - pbWait.setVisibility(View.GONE); - grpReady.setVisibility(View.VISIBLE); - if (message == null || message.ui_hide) { // Message gone (moved, deleted) if (FragmentMessage.this.isVisible()) @@ -236,10 +231,6 @@ public class FragmentMessage extends Fragment { tvSubject.setTypeface(null, visibility); tvCount.setTypeface(null, visibility); - // Observe attachments - db.attachment().liveAttachments(id).removeObservers(FragmentMessage.this); - db.attachment().liveAttachments(id).observe(FragmentMessage.this, attachmentsObserver); - top_navigation.getMenu().findItem(R.id.action_thread).setVisible(message.count > 1); MenuItem actionSeen = top_navigation.getMenu().findItem(R.id.action_seen); @@ -248,69 +239,52 @@ public class FragmentMessage extends Fragment { : R.drawable.baseline_visibility_24); actionSeen.setTitle(message.ui_seen ? R.string.title_unseen : R.string.title_seen); - bottom_navigation.getMenu().findItem(R.id.action_spam).setVisible(message.account != null); - bottom_navigation.getMenu().findItem(R.id.action_archive).setVisible(message.account != null); tvBody.setText(message.body == null ? null : Html.fromHtml(HtmlHelper.sanitize(getContext(), message.body, false))); } + + pbWait.setVisibility(View.GONE); + grpReady.setVisibility(View.VISIBLE); } }); + // Setup attachments and bottom toolbar + getLoaderManager().restartLoader(ActivityView.LOADER_MESSAGE_INIT, getArguments(), metaLoaderCallbacks).forceLoad(); + return view; } @Override public void onResume() { super.onResume(); - liveFolder.observe(this, folderObserver); - } - @Override - public void onPause() { - super.onPause(); - liveFolder.removeObservers(this); + // Set subtitle + getLoaderManager().restartLoader(ActivityView.LOADER_MESSAGE_INIT, getArguments(), metaLoaderCallbacks).forceLoad(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_cc, menu); + inflater.inflate(R.menu.menu_address, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_cc: - onMenuCc(); + case R.id.menu_address: + onMenuAddress(); return true; default: return super.onOptionsItemSelected(item); } } - private void onMenuCc() { + private void onMenuAddress() { if (grpReady.getVisibility() == View.VISIBLE) grpAddress.setVisibility(grpAddress.getVisibility() == View.GONE ? View.VISIBLE : View.GONE); } - Observer folderObserver = new Observer() { - @Override - public void onChanged(@Nullable TupleFolderEx folder) { - ((AppCompatActivity) getActivity()).getSupportActionBar().setSubtitle(folder == null - ? null - : Helper.localizeFolderName(getContext(), folder)); - } - }; - - Observer> attachmentsObserver = new Observer>() { - @Override - public void onChanged(@Nullable List attachments) { - adapter.set(attachments); - grpAttachments.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE); - } - }; - private void onActionSeen(final long id) { executor.submit(new Runnable() { @Override @@ -357,30 +331,50 @@ public class FragmentMessage extends Fragment { } private void onActionDelete(final long id) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder - .setMessage(R.string.title_ask_delete) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - executor.submit(new Runnable() { - @Override - public void run() { - try { - DB db = DB.getInstance(getContext()); - EntityMessage message = db.message().getMessage(id); - message.ui_hide = true; - db.message().updateMessage(message); - - EntityOperation.queue(getContext(), message, EntityOperation.DELETE); - } catch (Throwable ex) { - Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + String folderType = (String) bottom_navigation.getTag(); + if (EntityFolder.TYPE_TRASH.equals(folderType)) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder + .setMessage(R.string.title_ask_delete) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + executor.submit(new Runnable() { + @Override + public void run() { + try { + DB db = DB.getInstance(getContext()); + EntityMessage message = db.message().getMessage(id); + message.ui_hide = true; + db.message().updateMessage(message); + + EntityOperation.queue(getContext(), message, EntityOperation.DELETE); + } catch (Throwable ex) { + Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + } } - } - }); + }); + } + }) + .setNegativeButton(android.R.string.cancel, null).show(); + } else { + executor.submit(new Runnable() { + @Override + public void run() { + try { + DB db = DB.getInstance(getContext()); + EntityMessage message = db.message().getMessage(id); + message.ui_hide = true; + db.message().updateMessage(message); + + EntityFolder trash = db.folder().getFolderByType(message.account, EntityFolder.TYPE_TRASH); + EntityOperation.queue(getContext(), message, EntityOperation.MOVE, trash.id); + } catch (Throwable ex) { + Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } - }) - .setNegativeButton(android.R.string.cancel, null).show(); + } + }); + } } private void onActionSpam(final long id) { @@ -396,15 +390,10 @@ public class FragmentMessage extends Fragment { try { DB db = DB.getInstance(getContext()); EntityMessage message = db.message().getMessage(id); - EntityFolder spam = db.folder().getSpamFolder(message.account); - if (spam == null) { - Toast.makeText(getContext(), R.string.title_no_spam, Toast.LENGTH_LONG).show(); - return; - } - message.ui_hide = true; db.message().updateMessage(message); + EntityFolder spam = db.folder().getFolderByType(message.account, EntityFolder.TYPE_JUNK); EntityOperation.queue(getContext(), message, EntityOperation.MOVE, spam.id); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); @@ -423,15 +412,10 @@ public class FragmentMessage extends Fragment { try { DB db = DB.getInstance(getContext()); EntityMessage message = db.message().getMessage(id); - EntityFolder archive = db.folder().getArchiveFolder(message.account); - if (archive == null) { - Toast.makeText(getContext(), R.string.title_no_archive, Toast.LENGTH_LONG).show(); - return; - } - message.ui_hide = true; db.message().updateMessage(message); + EntityFolder archive = db.folder().getFolderByType(message.account, EntityFolder.TYPE_ARCHIVE); EntityOperation.queue(getContext(), message, EntityOperation.MOVE, archive.id); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); @@ -445,4 +429,82 @@ public class FragmentMessage extends Fragment { .putExtra("id", id) .putExtra("action", "reply")); } + + + private static class MetaLoader extends AsyncTaskLoader { + private Bundle args; + + MetaLoader(Context context) { + super(context); + } + + void setArgs(Bundle args) { + this.args = args; + } + + @Override + public MetaData loadInBackground() { + MetaData result = new MetaData(); + try { + long id = args.getLong("id"); // message + + DB db = DB.getInstance(getContext()); + EntityMessage message = db.message().getMessage(id); + result.folder = db.folder().getFolder(message.folder); + result.hasTrash = (db.folder().getFolderByType(message.account, EntityFolder.TYPE_TRASH) != null); + result.hasJunk = (db.folder().getFolderByType(message.account, EntityFolder.TYPE_JUNK) != null); + result.hasArchive = (db.folder().getFolderByType(message.account, EntityFolder.TYPE_ARCHIVE) != null); + result.attachments = db.attachment().getAttachments(id); + } catch (Throwable ex) { + Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + result.ex = ex; + } + return result; + } + } + + private LoaderManager.LoaderCallbacks metaLoaderCallbacks = new LoaderManager.LoaderCallbacks() { + @NonNull + @Override + public Loader onCreateLoader(int id, Bundle args) { + MetaLoader loader = new MetaLoader(getContext()); + loader.setArgs(args); + return loader; + } + + @Override + public void onLoadFinished(@NonNull Loader loader, MetaData data) { + getLoaderManager().destroyLoader(loader.getId()); + + if (data.ex == null) { + ((AppCompatActivity) getActivity()).getSupportActionBar().setSubtitle(data.folder == null + ? null + : Helper.localizeFolderName(getContext(), data.folder.name)); + + adapter.set(data.attachments); + grpAttachments.setVisibility(data.attachments.size() > 0 ? View.VISIBLE : View.GONE); + + boolean outbox = EntityFolder.TYPE_OUTBOX.equals(data.folder.type); + + bottom_navigation.setTag(data.folder.type); // trash or delete + bottom_navigation.getMenu().findItem(R.id.action_delete).setVisible(data.hasJunk); + bottom_navigation.getMenu().findItem(R.id.action_spam).setVisible(!outbox && data.hasJunk); + bottom_navigation.getMenu().findItem(R.id.action_archive).setVisible(!outbox && data.hasArchive); + bottom_navigation.setVisibility(View.VISIBLE); + } + } + + @Override + public void onLoaderReset(@NonNull Loader loader) { + } + }; + + private static class MetaData { + Throwable ex; + EntityFolder folder; + boolean hasTrash; + boolean hasJunk; + boolean hasArchive; + List attachments; + } } diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 5ffa1540f9..31cca983db 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -796,7 +796,7 @@ public class ServiceSynchronize extends LifecycleService { } if (candidate) { Log.i(Helper.TAG, ifolder.getFullName() + " candidate attr=" + TextUtils.join(",", attrs)); - EntityFolder folder = dao.getFolder(account.id, ifolder.getFullName()); + EntityFolder folder = dao.getFolderByName(account.id, ifolder.getFullName()); if (folder == null) { folder = new EntityFolder(); folder.account = account.id; diff --git a/app/src/main/res/layout/fragment_message.xml b/app/src/main/res/layout/fragment_message.xml index 90612b54ef..3937285dd3 100644 --- a/app/src/main/res/layout/fragment_message.xml +++ b/app/src/main/res/layout/fragment_message.xml @@ -243,5 +243,5 @@ android:id="@+id/grpReady" android:layout_width="0dp" android:layout_height="0dp" - app:constraint_referenced_ids="tvFrom,tvTime,tvSubject,tvCount,top_navigation,scroll,bottom_navigation" /> + app:constraint_referenced_ids="tvFrom,tvTime,tvSubject,tvCount,top_navigation,scroll" /> diff --git a/app/src/main/res/menu/menu_cc.xml b/app/src/main/res/menu/menu_address.xml similarity index 88% rename from app/src/main/res/menu/menu_cc.xml rename to app/src/main/res/menu/menu_address.xml index 3135586fbf..82f8f786d5 100644 --- a/app/src/main/res/menu/menu_cc.xml +++ b/app/src/main/res/menu/menu_address.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d3177005b..7f9fea969c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -98,9 +98,6 @@ Delete message permanently? Report message as spam? - This account has no archive folder - This account has no spam folder - Compose From: To: