diff --git a/app/src/main/java/org/dystopia/email/FragmentMessages.java b/app/src/main/java/org/dystopia/email/FragmentMessages.java
index b8996f94..e2a92e42 100644
--- a/app/src/main/java/org/dystopia/email/FragmentMessages.java
+++ b/app/src/main/java/org/dystopia/email/FragmentMessages.java
@@ -1,24 +1,22 @@
package org.dystopia.email;
/*
- This file is part of FairEmail.
-
- FairEmail is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- FairEmail is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with FairEmail. If not, see .
-
- Copyright 2018, Marcel Bokhorst (M66B)
- Copyright 2018, Distopico (dystopia project) and contributors
-*/
+ * This file is part of FairEmail.
+ *
+ * FairEmail is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * FairEmail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with FairEmail. If not,
+ * see .
+ *
+ * Copyright 2018, Marcel Bokhorst (M66B) Copyright 2018, Distopico (dystopia project)
+ * and contributors
+ */
import android.content.Context;
import android.content.Intent;
@@ -149,9 +147,7 @@ public class FragmentMessages extends FragmentEx {
@Override
@Nullable
- public View onCreateView(
- @NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
view = (ViewGroup) inflater.inflate(R.layout.fragment_messages, container, false);
@@ -177,439 +173,381 @@ public class FragmentMessages extends FragmentEx {
// Wire controls
- ibHintSwipe.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- prefs.edit().putBoolean("message_swipe", true).apply();
- grpHintSwipe.setVisibility(View.GONE);
- }
- });
+ ibHintSwipe.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ prefs.edit().putBoolean("message_swipe", true).apply();
+ grpHintSwipe.setVisibility(View.GONE);
+ }
+ });
- ibHintSelect.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- prefs.edit().putBoolean("message_select", true).apply();
- grpHintSelect.setVisibility(View.GONE);
- }
- });
+ ibHintSelect.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ prefs.edit().putBoolean("message_select", true).apply();
+ grpHintSelect.setVisibility(View.GONE);
+ }
+ });
- ibHintSupport.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- prefs.edit().putBoolean("app_support", true).apply();
- grpHintSupport.setVisibility(View.GONE);
- }
- });
+ ibHintSupport.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ prefs.edit().putBoolean("app_support", true).apply();
+ grpHintSupport.setVisibility(View.GONE);
+ }
+ });
rvMessage.setHasFixedSize(false);
LinearLayoutManager llm = new LinearLayoutManager(getContext());
rvMessage.setLayoutManager(llm);
- adapter =
- new AdapterMessage(
- getContext(),
- getViewLifecycleOwner(),
- getFragmentManager(),
- viewType,
- folder,
- new AdapterMessage.IProperties() {
- @Override
- public void setExpanded(long id, boolean expand) {
- if (expand) {
- expanded.add(id);
- handleExpand(id);
- } else {
- expanded.remove(id);
- }
- }
+ adapter = new AdapterMessage(getContext(), getViewLifecycleOwner(), getFragmentManager(),
+ viewType, folder, new AdapterMessage.IProperties() {
+ @Override
+ public void setExpanded(long id, boolean expand) {
+ if (expand) {
+ expanded.add(id);
+ handleExpand(id);
+ } else {
+ expanded.remove(id);
+ }
+ }
- @Override
- public void setDetails(long id, boolean show) {
- if (show) {
- details.add(id);
- } else {
- details.remove(id);
- }
- }
+ @Override
+ public void setDetails(long id, boolean show) {
+ if (show) {
+ details.add(id);
+ } else {
+ details.remove(id);
+ }
+ }
- @Override
- public void setHeaders(long id, boolean show) {
- if (show) {
- headers.add(id);
- } else {
- headers.remove(id);
- }
- }
+ @Override
+ public void setHeaders(long id, boolean show) {
+ if (show) {
+ headers.add(id);
+ } else {
+ headers.remove(id);
+ }
+ }
- @Override
- public void setImages(long id, boolean show) {
- if (show) {
- images.add(id);
- } else {
- images.remove(id);
- }
- }
+ @Override
+ public void setImages(long id, boolean show) {
+ if (show) {
+ images.add(id);
+ } else {
+ images.remove(id);
+ }
+ }
- @Override
- public boolean isExpanded(long id) {
- return expanded.contains(id);
- }
+ @Override
+ public boolean isExpanded(long id) {
+ return expanded.contains(id);
+ }
- @Override
- public boolean showDetails(long id) {
- return details.contains(id);
- }
+ @Override
+ public boolean showDetails(long id) {
+ return details.contains(id);
+ }
- @Override
- public boolean showHeaders(long id) {
- return headers.contains(id);
- }
+ @Override
+ public boolean showHeaders(long id) {
+ return headers.contains(id);
+ }
- @Override
- public boolean showImages(long id) {
- return images.contains(id);
- }
- });
+ @Override
+ public boolean showImages(long id) {
+ return images.contains(id);
+ }
+ });
rvMessage.setAdapter(adapter);
if (viewType == AdapterMessage.ViewType.FOLDER) {
- selectionTracker =
- new SelectionTracker.Builder<>(
- "messages-selection",
- rvMessage,
- new ItemKeyProviderMessage(rvMessage),
- new ItemDetailsLookupMessage(rvMessage),
- StorageStrategy.createLongStorage())
- .withSelectionPredicate(new SelectionPredicateMessage(rvMessage))
- .build();
+ selectionTracker = new SelectionTracker.Builder<>("messages-selection", rvMessage,
+ new ItemKeyProviderMessage(rvMessage), new ItemDetailsLookupMessage(rvMessage),
+ StorageStrategy.createLongStorage())
+ .withSelectionPredicate(new SelectionPredicateMessage(rvMessage)).build();
adapter.setSelectionTracker(selectionTracker);
- selectionTracker.addObserver(
- new SelectionTracker.SelectionObserver() {
- @Override
- public void onSelectionChanged() {
- if (selectionTracker.hasSelection()) {
- fabMove.show();
- } else {
- fabMove.hide();
- }
- }
- });
+ selectionTracker.addObserver(new SelectionTracker.SelectionObserver() {
+ @Override
+ public void onSelectionChanged() {
+ if (selectionTracker.hasSelection()) {
+ fabMove.show();
+ } else {
+ fabMove.hide();
+ }
+ }
+ });
}
- new ItemTouchHelper(
- new ItemTouchHelper.Callback() {
- @Override
- public int getMovementFlags(
- RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
- if (!prefs.getBoolean("swipe", true)) {
- return 0;
- }
+ new ItemTouchHelper(new ItemTouchHelper.Callback() {
+ @Override
+ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ if (!prefs.getBoolean("swipe", true)) {
+ return 0;
+ }
- if (selectionTracker != null && selectionTracker.hasSelection()) {
- return 0;
- }
+ if (selectionTracker != null && selectionTracker.hasSelection()) {
+ return 0;
+ }
- int pos = viewHolder.getAdapterPosition();
- if (pos == RecyclerView.NO_POSITION) {
- return 0;
- }
+ int pos = viewHolder.getAdapterPosition();
+ if (pos == RecyclerView.NO_POSITION) {
+ return 0;
+ }
- TupleMessageEx message =
- ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos);
- if (message == null
- || expanded.contains(message.id)
- || EntityFolder.OUTBOX.equals(message.folderType)) {
- return 0;
- }
+ TupleMessageEx message =
+ ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos);
+ if (message == null || expanded.contains(message.id)
+ || EntityFolder.OUTBOX.equals(message.folderType)) {
+ return 0;
+ }
- int flags = 0;
- if (archives.contains(message.account)) {
- flags |= ItemTouchHelper.RIGHT;
- }
- if (trashes.contains(message.account)) {
- flags |= ItemTouchHelper.LEFT;
- }
+ int flags = 0;
+ if (archives.contains(message.account)) {
+ flags |= ItemTouchHelper.RIGHT;
+ }
+ if (trashes.contains(message.account)) {
+ flags |= ItemTouchHelper.LEFT;
+ }
- return makeMovementFlags(0, flags);
- }
+ return makeMovementFlags(0, flags);
+ }
- @Override
- public boolean onMove(
- RecyclerView recyclerView,
- RecyclerView.ViewHolder viewHolder,
- RecyclerView.ViewHolder target) {
- return false;
- }
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
+ RecyclerView.ViewHolder target) {
+ return false;
+ }
- @Override
- public void onChildDraw(
- Canvas canvas,
- RecyclerView recyclerView,
- RecyclerView.ViewHolder viewHolder,
- float dX,
- float dY,
- int actionState,
- boolean isCurrentlyActive) {
- int pos = viewHolder.getAdapterPosition();
- if (pos == RecyclerView.NO_POSITION) {
- return;
- }
+ @Override
+ public void onChildDraw(Canvas canvas, RecyclerView recyclerView,
+ RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
+ boolean isCurrentlyActive) {
+ int pos = viewHolder.getAdapterPosition();
+ if (pos == RecyclerView.NO_POSITION) {
+ return;
+ }
- TupleMessageEx message =
- ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos);
- if (message == null) {
- return;
- }
+ TupleMessageEx message =
+ ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos);
+ if (message == null) {
+ return;
+ }
- boolean inbox =
- (EntityFolder.ARCHIVE.equals(message.folderType)
- || EntityFolder.TRASH.equals(message.folderType));
-
- View itemView = viewHolder.itemView;
- int margin = Math.round(12 * (getResources().getDisplayMetrics().density));
-
- if (dX > margin) {
- // Right swipe
- Drawable d =
- getResources()
- .getDrawable(
- inbox
- ? R.drawable.baseline_move_to_inbox_24
- : R.drawable.baseline_archive_24,
- getContext().getTheme());
- int padding = (itemView.getHeight() - d.getIntrinsicHeight());
- d.setBounds(
- itemView.getLeft() + margin,
- itemView.getTop() + padding / 2,
- itemView.getLeft() + margin + d.getIntrinsicWidth(),
- itemView.getTop() + padding / 2 + d.getIntrinsicHeight());
- d.draw(canvas);
- } else if (dX < -margin) {
- // Left swipe
- Drawable d =
- getResources()
- .getDrawable(
- inbox
- ? R.drawable.baseline_move_to_inbox_24
- : R.drawable.baseline_delete_24,
- getContext().getTheme());
- int padding = (itemView.getHeight() - d.getIntrinsicHeight());
- d.setBounds(
- itemView.getLeft() + itemView.getWidth() - d.getIntrinsicWidth() - margin,
- itemView.getTop() + padding / 2,
- itemView.getLeft() + itemView.getWidth() - margin,
- itemView.getTop() + padding / 2 + d.getIntrinsicHeight());
- d.draw(canvas);
- }
+ boolean inbox = (EntityFolder.ARCHIVE.equals(message.folderType)
+ || EntityFolder.TRASH.equals(message.folderType));
+
+ View itemView = viewHolder.itemView;
+ int margin = Math.round(12 * (getResources().getDisplayMetrics().density));
+
+ if (dX > margin) {
+ // Right swipe
+ Drawable d = getResources().getDrawable(
+ inbox ? R.drawable.baseline_move_to_inbox_24 : R.drawable.baseline_archive_24,
+ getContext().getTheme());
+ int padding = (itemView.getHeight() - d.getIntrinsicHeight());
+ d.setBounds(itemView.getLeft() + margin, itemView.getTop() + padding / 2,
+ itemView.getLeft() + margin + d.getIntrinsicWidth(),
+ itemView.getTop() + padding / 2 + d.getIntrinsicHeight());
+ d.draw(canvas);
+ } else if (dX < -margin) {
+ // Left swipe
+ Drawable d = getResources().getDrawable(
+ inbox ? R.drawable.baseline_move_to_inbox_24 : R.drawable.baseline_delete_24,
+ getContext().getTheme());
+ int padding = (itemView.getHeight() - d.getIntrinsicHeight());
+ d.setBounds(itemView.getLeft() + itemView.getWidth() - d.getIntrinsicWidth() - margin,
+ itemView.getTop() + padding / 2, itemView.getLeft() + itemView.getWidth() - margin,
+ itemView.getTop() + padding / 2 + d.getIntrinsicHeight());
+ d.draw(canvas);
+ }
- super.onChildDraw(
- canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
- }
+ super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
+ }
- @Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
- int pos = viewHolder.getAdapterPosition();
- if (pos == RecyclerView.NO_POSITION) {
- return;
- }
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+ int pos = viewHolder.getAdapterPosition();
+ if (pos == RecyclerView.NO_POSITION) {
+ return;
+ }
- TupleMessageEx message =
- ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos);
- if (message == null) {
- return;
- }
- Log.i(Helper.TAG, "Swiped dir=" + direction + " message=" + message.id);
+ TupleMessageEx message =
+ ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos);
+ if (message == null) {
+ return;
+ }
+ Log.i(Helper.TAG, "Swiped dir=" + direction + " message=" + message.id);
- Bundle args = new Bundle();
- args.putLong("id", message.id);
- args.putBoolean("thread", viewType != AdapterMessage.ViewType.THREAD);
- args.putInt("direction", direction);
+ Bundle args = new Bundle();
+ args.putLong("id", message.id);
+ args.putBoolean("thread", viewType != AdapterMessage.ViewType.THREAD);
+ args.putInt("direction", direction);
- new SimpleTask() {
- @Override
- protected MessageTarget onLoad(Context context, Bundle args) {
- long id = args.getLong("id");
- boolean thread = args.getBoolean("thread");
- int direction = args.getInt("direction");
+ new SimpleTask() {
+ @Override
+ protected MessageTarget onLoad(Context context, Bundle args) {
+ long id = args.getLong("id");
+ boolean thread = args.getBoolean("thread");
+ int direction = args.getInt("direction");
- MessageTarget result = new MessageTarget();
- EntityFolder target = null;
+ MessageTarget result = new MessageTarget();
+ EntityFolder target = null;
- // Get target folder and hide message
- DB db = DB.getInstance(context);
- try {
- db.beginTransaction();
+ // Get target folder and hide message
+ DB db = DB.getInstance(context);
+ try {
+ db.beginTransaction();
- EntityMessage message = db.message().getMessage(id);
+ EntityMessage message = db.message().getMessage(id);
- EntityFolder folder = db.folder().getFolder(message.folder);
- if (EntityFolder.ARCHIVE.equals(folder.type)
- || EntityFolder.TRASH.equals(folder.type)) {
- target = db.folder().getFolderByType(message.account, EntityFolder.INBOX);
- } else {
- if (direction == ItemTouchHelper.RIGHT) {
- target =
- db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE);
- }
- if (direction == ItemTouchHelper.LEFT || target == null) {
- target = db.folder().getFolderByType(message.account, EntityFolder.TRASH);
- }
- if (target == null) {
- target = db.folder().getFolderByType(message.account, EntityFolder.INBOX);
- }
- }
+ EntityFolder folder = db.folder().getFolder(message.folder);
+ if (EntityFolder.ARCHIVE.equals(folder.type)
+ || EntityFolder.TRASH.equals(folder.type)) {
+ target = db.folder().getFolderByType(message.account, EntityFolder.INBOX);
+ } else {
+ if (direction == ItemTouchHelper.RIGHT) {
+ target = db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE);
+ }
+ if (direction == ItemTouchHelper.LEFT || target == null) {
+ target = db.folder().getFolderByType(message.account, EntityFolder.TRASH);
+ }
+ if (target == null) {
+ target = db.folder().getFolderByType(message.account, EntityFolder.INBOX);
+ }
+ }
- result.target = target.name;
- result.display = (target.display == null ? target.name : target.display);
+ result.target = target.name;
+ result.display = (target.display == null ? target.name : target.display);
- if (thread) {
- List messages =
- db.message().getMessageByThread(message.account, message.thread);
- for (EntityMessage threaded : messages) {
- if (!threaded.ui_hide && threaded.folder.equals(message.folder)) {
- result.ids.add(threaded.id);
- }
- }
- } else {
- result.ids.add(message.id);
- }
+ if (thread) {
+ List messages =
+ db.message().getMessageByThread(message.account, message.thread);
+ for (EntityMessage threaded : messages) {
+ if (!threaded.ui_hide && threaded.folder.equals(message.folder)) {
+ result.ids.add(threaded.id);
+ }
+ }
+ } else {
+ result.ids.add(message.id);
+ }
- for (long mid : result.ids) {
- Log.i(Helper.TAG, "Move hide id=" + mid + " target=" + result.target);
- db.message().setMessageUiHide(mid, true);
- }
+ for (long mid : result.ids) {
+ Log.i(Helper.TAG, "Move hide id=" + mid + " target=" + result.target);
+ db.message().setMessageUiHide(mid, true);
+ }
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
- return result;
- }
+ return result;
+ }
- @Override
- protected void onLoaded(final Bundle args, final MessageTarget result) {
- // Show undo snackbar
- final Snackbar snackbar =
- Snackbar.make(
- view,
- getString(
- R.string.title_moving,
- Helper.localizeFolderName(getContext(), result.display)),
- Snackbar.LENGTH_INDEFINITE);
- snackbar.setAction(
- R.string.title_undo,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- snackbar.dismiss();
-
- Bundle args = new Bundle();
- args.putSerializable("result", result);
-
- // Show message again
- new SimpleTask() {
- @Override
- protected Void onLoad(Context context, Bundle args) {
- MessageTarget result =
- (MessageTarget) args.getSerializable("result");
- for (long id : result.ids) {
- Log.i(Helper.TAG, "Move undo id=" + id);
- DB.getInstance(context).message().setMessageUiHide(id, false);
- }
- return null;
- }
-
- @Override
- protected void onException(Bundle args, Throwable ex) {
- super.onException(args, ex);
- }
- }.load(FragmentMessages.this, args);
- }
- });
- snackbar.show();
-
- // Wait
- new Handler()
- .postDelayed(
- new Runnable() {
- @Override
- public void run() {
- Log.i(Helper.TAG, "Move timeout");
-
- // Remove snackbar
- if (snackbar.isShown()) {
- snackbar.dismiss();
- }
+ @Override
+ protected void onLoaded(final Bundle args, final MessageTarget result) {
+ // Show undo snackbar
+ final Snackbar snackbar = Snackbar.make(view,
+ getString(R.string.title_moving,
+ Helper.localizeFolderName(getContext(), result.display)),
+ Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction(R.string.title_undo, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ snackbar.dismiss();
- final Bundle args = new Bundle();
- args.putSerializable("result", result);
-
- // Process move in a thread
- // - the fragment could be gone
- executor.submit(
- new Runnable() {
- @Override
- public void run() {
- try {
- MessageTarget result =
- (MessageTarget) args.getSerializable("result");
-
- DB db = DB.getInstance(snackbar.getContext());
- try {
- db.beginTransaction();
-
- for (long id : result.ids) {
- EntityMessage message = db.message().getMessage(id);
- if (message != null && message.ui_hide) {
- Log.i(
- Helper.TAG,
- "Move id=" + id + " target=" + result.target);
- EntityFolder folder =
- db.folder()
- .getFolderByName(
- message.account, result.target);
- EntityOperation.queue(
- db, message, EntityOperation.MOVE, folder.id);
- }
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
-
- EntityOperation.process(snackbar.getContext());
-
- } catch (Throwable ex) {
- Log.e(
- Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
- }
- }
- });
- }
- },
- UNDO_TIMEOUT);
+ Bundle args = new Bundle();
+ args.putSerializable("result", result);
+
+ // Show message again
+ new SimpleTask() {
+ @Override
+ protected Void onLoad(Context context, Bundle args) {
+ MessageTarget result = (MessageTarget) args.getSerializable("result");
+ for (long id : result.ids) {
+ Log.i(Helper.TAG, "Move undo id=" + id);
+ DB.getInstance(context).message().setMessageUiHide(id, false);
+ }
+ return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
- Helper.unexpectedError(getContext(), ex);
+ super.onException(args, ex);
}
}.load(FragmentMessages.this, args);
}
+ });
+ snackbar.show();
+
+ // Wait
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ Log.i(Helper.TAG, "Move timeout");
+
+ // Remove snackbar
+ if (snackbar.isShown()) {
+ snackbar.dismiss();
+ }
+
+ final Bundle args = new Bundle();
+ args.putSerializable("result", result);
+
+ // Process move in a thread
+ // - the fragment could be gone
+ executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ MessageTarget result = (MessageTarget) args.getSerializable("result");
+
+ DB db = DB.getInstance(snackbar.getContext());
+ try {
+ db.beginTransaction();
+
+ for (long id : result.ids) {
+ EntityMessage message = db.message().getMessage(id);
+ if (message != null && message.ui_hide) {
+ Log.i(Helper.TAG, "Move id=" + id + " target=" + result.target);
+ EntityFolder folder =
+ db.folder().getFolderByName(message.account, result.target);
+ EntityOperation.queue(db, message, EntityOperation.MOVE, folder.id);
+ }
+ }
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
- class MessageTarget implements Serializable {
- List ids = new ArrayList<>();
- String target;
- String display;
+ EntityOperation.process(snackbar.getContext());
+
+ } catch (Throwable ex) {
+ Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
+ }
+ }
+ });
}
- })
- .attachToRecyclerView(rvMessage);
+ }, UNDO_TIMEOUT);
+ }
+
+ @Override
+ protected void onException(Bundle args, Throwable ex) {
+ Helper.unexpectedError(getContext(), ex);
+ }
+ }.load(FragmentMessages.this, args);
+ }
+
+ class MessageTarget implements Serializable {
+ List ids = new ArrayList<>();
+ String target;
+ String display;
+ }
+ }).attachToRecyclerView(rvMessage);
bottom_navigation.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
@@ -619,170 +557,154 @@ public class FragmentMessages extends FragmentEx {
ViewModelMessages.Target target =
(menuItem.getItemId() == R.id.action_prev ? pn[0] : pn[1]);
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
- lbm.sendBroadcast(
- new Intent(ActivityView.ACTION_VIEW_THREAD)
- .putExtra("account", target.account)
- .putExtra("thread", target.thread));
+ lbm.sendBroadcast(new Intent(ActivityView.ACTION_VIEW_THREAD)
+ .putExtra("account", target.account).putExtra("thread", target.thread));
return true;
}
});
- fab.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- startActivity(
- new Intent(getContext(), ActivityCompose.class)
- .putExtra("action", "new")
- .putExtra("account", (Long) fab.getTag()));
- }
- });
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(getContext(), ActivityCompose.class).putExtra("action", "new")
+ .putExtra("account", (Long) fab.getTag()));
+ }
+ });
+
+ fabMove.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Bundle args = new Bundle();
+ args.putLong("folder", folder);
- fabMove.setOnClickListener(
- new View.OnClickListener() {
+ new SimpleTask>() {
@Override
- public void onClick(View v) {
- Bundle args = new Bundle();
- args.putLong("folder", folder);
+ protected List onLoad(Context context, Bundle args) {
+ long folder = args.getLong("folder");
+ DB db = DB.getInstance(context);
+
+ EntityFolder source = db.folder().getFolder(folder);
+ List folders = db.folder().getFolders(source.account);
+ List targets = new ArrayList<>();
+ for (EntityFolder f : folders) {
+ if (!f.id.equals(folder) && !EntityFolder.DRAFTS.equals(f.type)) {
+ targets.add(f);
+ }
+ }
+
+ final Collator collator = Collator.getInstance(Locale.getDefault());
+ collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents
+ // etc
- new SimpleTask>() {
+ Collections.sort(targets, new Comparator() {
@Override
- protected List onLoad(Context context, Bundle args) {
- long folder = args.getLong("folder");
- DB db = DB.getInstance(context);
-
- EntityFolder source = db.folder().getFolder(folder);
- List folders = db.folder().getFolders(source.account);
- List targets = new ArrayList<>();
- for (EntityFolder f : folders) {
- if (!f.id.equals(folder) && !EntityFolder.DRAFTS.equals(f.type)) {
- targets.add(f);
- }
+ public int compare(EntityFolder f1, EntityFolder f2) {
+ int s = Integer.compare(EntityFolder.FOLDER_SORT_ORDER.indexOf(f1.type),
+ EntityFolder.FOLDER_SORT_ORDER.indexOf(f2.type));
+ if (s != 0) {
+ return s;
}
+ return collator.compare(f1.name == null ? "" : f1.name,
+ f2.name == null ? "" : f2.name);
+ }
+ });
- final Collator collator = Collator.getInstance(Locale.getDefault());
- collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents
- // etc
-
- Collections.sort(
- targets,
- new Comparator() {
- @Override
- public int compare(EntityFolder f1, EntityFolder f2) {
- int s =
- Integer.compare(
- EntityFolder.FOLDER_SORT_ORDER.indexOf(f1.type),
- EntityFolder.FOLDER_SORT_ORDER.indexOf(f2.type));
- if (s != 0) {
- return s;
- }
- return collator.compare(
- f1.name == null ? "" : f1.name, f2.name == null ? "" : f2.name);
- }
- });
+ return targets;
+ }
- return targets;
- }
+ @Override
+ protected void onLoaded(final Bundle args, List folders) {
+ PopupMenu popupMenu = new PopupMenu(getContext(), popupAnchor);
+
+ int order = 0;
+ for (EntityFolder folder : folders) {
+ String name =
+ (folder.display == null ? Helper.localizeFolderName(getContext(), folder.name)
+ : folder.display);
+ popupMenu.getMenu().add(Menu.NONE, folder.id.intValue(), order++, name);
+ }
+ popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
- protected void onLoaded(final Bundle args, List folders) {
- PopupMenu popupMenu = new PopupMenu(getContext(), popupAnchor);
-
- int order = 0;
- for (EntityFolder folder : folders) {
- String name =
- (folder.display == null
- ? Helper.localizeFolderName(getContext(), folder.name)
- : folder.display);
- popupMenu.getMenu().add(Menu.NONE, folder.id.intValue(), order++, name);
+ public boolean onMenuItemClick(final MenuItem target) {
+ MutableSelection selection = new MutableSelection<>();
+ selectionTracker.copySelection(selection);
+
+ long[] ids = new long[selection.size()];
+ int i = 0;
+ for (Long id : selection) {
+ ids[i++] = id;
}
- popupMenu.setOnMenuItemClickListener(
- new PopupMenu.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(final MenuItem target) {
- MutableSelection selection = new MutableSelection<>();
- selectionTracker.copySelection(selection);
-
- long[] ids = new long[selection.size()];
- int i = 0;
- for (Long id : selection) {
- ids[i++] = id;
- }
+ selectionTracker.clearSelection();
- selectionTracker.clearSelection();
-
- args.putLongArray("ids", ids);
- args.putLong("target", target.getItemId());
-
- new SimpleTask() {
- @Override
- protected Void onLoad(Context context, Bundle args) {
- long[] ids = args.getLongArray("ids");
- long target = args.getLong("target");
-
- DB db = DB.getInstance(context);
- try {
- db.beginTransaction();
-
- for (long id : ids) {
- EntityMessage message = db.message().getMessage(id);
- List messages =
- db.message()
- .getMessageByThread(message.account, message.thread);
- for (EntityMessage threaded : messages) {
- if (threaded.folder.equals(message.folder)) {
- db.message().setMessageUiHide(threaded.id, true);
- EntityOperation.queue(
- db, threaded, EntityOperation.MOVE, target);
- }
- }
- }
+ args.putLongArray("ids", ids);
+ args.putLong("target", target.getItemId());
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
+ new SimpleTask() {
+ @Override
+ protected Void onLoad(Context context, Bundle args) {
+ long[] ids = args.getLongArray("ids");
+ long target = args.getLong("target");
- EntityOperation.process(context);
+ DB db = DB.getInstance(context);
+ try {
+ db.beginTransaction();
- return null;
+ for (long id : ids) {
+ EntityMessage message = db.message().getMessage(id);
+ List messages =
+ db.message().getMessageByThread(message.account, message.thread);
+ for (EntityMessage threaded : messages) {
+ if (threaded.folder.equals(message.folder)) {
+ db.message().setMessageUiHide(threaded.id, true);
+ EntityOperation.queue(db, threaded, EntityOperation.MOVE, target);
}
+ }
+ }
- @Override
- protected void onException(Bundle args, Throwable ex) {
- Helper.unexpectedError(getContext(), ex);
- }
- }.load(FragmentMessages.this, args);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
- return true;
- }
- });
+ EntityOperation.process(context);
- popupMenu.show();
- }
+ return null;
+ }
- @Override
- protected void onException(Bundle args, Throwable ex) {
- Helper.unexpectedError(getContext(), ex);
- }
- }.load(FragmentMessages.this, args);
- }
- });
+ @Override
+ protected void onException(Bundle args, Throwable ex) {
+ Helper.unexpectedError(getContext(), ex);
+ }
+ }.load(FragmentMessages.this, args);
- ((ActivityBase) getActivity())
- .addBackPressedListener(
- new ActivityBase.IBackPressedListener() {
- @Override
- public boolean onBackPressed() {
- if (selectionTracker != null && selectionTracker.hasSelection()) {
- selectionTracker.clearSelection();
- return true;
- }
- return false;
+ return true;
}
});
+ popupMenu.show();
+ }
+
+ @Override
+ protected void onException(Bundle args, Throwable ex) {
+ Helper.unexpectedError(getContext(), ex);
+ }
+ }.load(FragmentMessages.this, args);
+ }
+ });
+
+ ((ActivityBase) getActivity()).addBackPressedListener(new ActivityBase.IBackPressedListener() {
+ @Override
+ public boolean onBackPressed() {
+ if (selectionTracker != null && selectionTracker.hasSelection()) {
+ selectionTracker.clearSelection();
+ return true;
+ }
+ return false;
+ }
+ });
+
// Initialize
tvNoEmail.setVisibility(View.GONE);
bottom_navigation.setVisibility(View.GONE);
@@ -840,71 +762,61 @@ public class FragmentMessages extends FragmentEx {
final DB db = DB.getInstance(getContext());
// Primary account
- db.account()
- .livePrimaryAccount()
- .observe(
- getViewLifecycleOwner(),
- new Observer() {
- @Override
- public void onChanged(EntityAccount account) {
- primary = (account == null ? -1 : account.id);
- connected = (account != null && "connected".equals(account.state));
- getActivity().invalidateOptionsMenu();
- }
- });
+ db.account().livePrimaryAccount().observe(getViewLifecycleOwner(),
+ new Observer() {
+ @Override
+ public void onChanged(EntityAccount account) {
+ primary = (account == null ? -1 : account.id);
+ connected = (account != null && "connected".equals(account.state));
+ getActivity().invalidateOptionsMenu();
+ }
+ });
// Folder
switch (viewType) {
case UNIFIED:
- db.folder()
- .liveUnified()
- .observe(
- getViewLifecycleOwner(),
- new Observer>() {
- @Override
- public void onChanged(List folders) {
- int unseen = 0;
- if (folders != null) {
- for (TupleFolderEx folder : folders) {
- unseen += folder.unseen;
- }
- }
- String name = getString(R.string.title_folder_unified);
- if (unseen > 0) {
- setSubtitle(getString(R.string.title_folder_unseen, name, unseen));
- } else {
- setSubtitle(name);
- }
+ db.folder().liveUnified().observe(getViewLifecycleOwner(),
+ new Observer>() {
+ @Override
+ public void onChanged(List folders) {
+ int unseen = 0;
+ if (folders != null) {
+ for (TupleFolderEx folder : folders) {
+ unseen += folder.unseen;
}
- });
+ }
+ String name = getString(R.string.title_folder_unified);
+ if (unseen > 0) {
+ setSubtitle(getString(R.string.title_folder_unseen, name, unseen));
+ } else {
+ setSubtitle(name);
+ }
+ }
+ });
break;
case FOLDER:
- db.folder()
- .liveFolderEx(folder)
- .observe(
- getViewLifecycleOwner(),
- new Observer() {
- @Override
- public void onChanged(@Nullable TupleFolderEx folder) {
- if (folder == null) {
- setSubtitle(null);
- } else {
- String name =
- (folder.display == null
- ? Helper.localizeFolderName(getContext(), folder.name)
- : folder.display);
- if (folder.unseen > 0) {
- setSubtitle(getString(R.string.title_folder_unseen, name, folder.unseen));
- } else {
- setSubtitle(name);
- }
-
- outbox = EntityFolder.OUTBOX.equals(folder.type);
- getActivity().invalidateOptionsMenu();
- }
+ db.folder().liveFolderEx(folder).observe(getViewLifecycleOwner(),
+ new Observer() {
+ @Override
+ public void onChanged(@Nullable TupleFolderEx folder) {
+ if (folder == null) {
+ setSubtitle(null);
+ } else {
+ String name =
+ (folder.display == null ? Helper.localizeFolderName(getContext(), folder.name)
+ : folder.display);
+ if (folder.unseen > 0) {
+ setSubtitle(getString(R.string.title_folder_unseen, name, folder.unseen));
+ } else {
+ setSubtitle(name);
}
- });
+
+ outbox = EntityFolder.OUTBOX.equals(folder.type);
+ getActivity().invalidateOptionsMenu();
+ }
+ }
+ });
break;
case THREAD:
@@ -917,31 +829,28 @@ public class FragmentMessages extends FragmentEx {
}
// Folders and messages
- db.folder()
- .liveSystemFolders(account)
- .observe(
- getViewLifecycleOwner(),
- new Observer>() {
- @Override
- public void onChanged(List folders) {
- if (folders == null) {
- folders = new ArrayList<>();
- }
-
- archives.clear();
- trashes.clear();
+ db.folder().liveSystemFolders(account).observe(getViewLifecycleOwner(),
+ new Observer>() {
+ @Override
+ public void onChanged(List folders) {
+ if (folders == null) {
+ folders = new ArrayList<>();
+ }
- for (EntityFolder folder : folders) {
- if (EntityFolder.ARCHIVE.equals(folder.type)) {
- archives.add(folder.account);
- } else if (EntityFolder.TRASH.equals(folder.type)) {
- trashes.add(folder.account);
- }
- }
+ archives.clear();
+ trashes.clear();
- loadMessages();
+ for (EntityFolder folder : folders) {
+ if (EntityFolder.ARCHIVE.equals(folder.type)) {
+ archives.add(folder.account);
+ } else if (EntityFolder.TRASH.equals(folder.type)) {
+ trashes.add(folder.account);
}
- });
+ }
+
+ loadMessages();
+ }
+ });
if (selectionTracker != null && selectionTracker.hasSelection()) {
fabMove.show();
@@ -998,42 +907,41 @@ public class FragmentMessages extends FragmentEx {
final MenuItem menuSearch = menu.findItem(R.id.menu_search);
final SearchView searchView = (SearchView) menuSearch.getActionView();
searchView.setQueryHint(getString(R.string.title_search_hint));
- searchView.setOnQueryTextListener(
- new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- menuSearch.collapseActionView();
-
- Bundle args = new Bundle();
- args.putLong("folder", folder);
- args.putString("search", query);
-
- new SimpleTask() {
- @Override
- protected Void onLoad(Context context, Bundle args) {
- DB.getInstance(context).message().deleteFoundMessages();
- return null;
- }
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ menuSearch.collapseActionView();
- @Override
- protected void onLoaded(Bundle args, Void data) {
- FragmentMessages fragment = new FragmentMessages();
- fragment.setArguments(args);
+ Bundle args = new Bundle();
+ args.putLong("folder", folder);
+ args.putString("search", query);
- FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
- fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("search");
- fragmentTransaction.commit();
- }
- }.load(FragmentMessages.this, args);
-
- return true;
+ new SimpleTask() {
+ @Override
+ protected Void onLoad(Context context, Bundle args) {
+ DB.getInstance(context).message().deleteFoundMessages();
+ return null;
}
@Override
- public boolean onQueryTextChange(String newText) {
- return false;
+ protected void onLoaded(Bundle args, Void data) {
+ FragmentMessages fragment = new FragmentMessages();
+ fragment.setArguments(args);
+
+ FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
+ fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("search");
+ fragmentTransaction.commit();
}
- });
+ }.load(FragmentMessages.this, args);
+
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return false;
+ }
+ });
super.onCreateOptionsMenu(menu, inflater);
}
@@ -1073,8 +981,7 @@ public class FragmentMessages extends FragmentEx {
case R.id.menu_sort_on_unread:
case R.id.menu_sort_on_starred:
- prefs
- .edit()
+ prefs.edit()
.putString("sort", item.getItemId() == R.id.menu_sort_on_unread ? "unread" : "starred")
.apply();
item.setChecked(true);
@@ -1170,83 +1077,21 @@ public class FragmentMessages extends FragmentEx {
switch (viewType) {
case UNIFIED:
- messages =
- new LivePagedListBuilder<>(
- db.message().pagedUnifiedInbox(sort, debug), LOCAL_PAGE_SIZE)
- .build();
+ messages = new LivePagedListBuilder<>(db.message().pagedUnifiedInbox(sort, debug),
+ LOCAL_PAGE_SIZE).build();
break;
case FOLDER:
if (searchCallback == null) {
- searchCallback =
- new BoundaryCallbackMessages(
- this,
- model,
- new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
- @Override
- public void onLoading() {
- pbWait.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onLoaded() {
- pbWait.setVisibility(View.GONE);
- }
-
- @Override
- public void onError(Context context, Throwable ex) {
- if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
- new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner())
- .setMessage(Helper.formatThrowable(ex))
- .setPositiveButton(android.R.string.cancel, null)
- .create()
- .show();
- }
- }
- });
- }
-
- PagedList.Config config =
- new PagedList.Config.Builder()
- .setPageSize(LOCAL_PAGE_SIZE)
- .setInitialLoadSizeHint(LOCAL_PAGE_SIZE)
- .setPrefetchDistance(REMOTE_PAGE_SIZE)
- .build();
- LivePagedListBuilder builder =
- new LivePagedListBuilder<>(
- db.message().pagedFolder(folder, folderType, sort, false, debug), config);
- if (browse) {
- builder.setBoundaryCallback(searchCallback);
- }
- messages = builder.build();
-
- break;
- case THREAD:
- messages =
- new LivePagedListBuilder<>(
- db.message().pagedThread(account, folder, thread, sort, debug),
- LOCAL_PAGE_SIZE)
- .build();
- break;
- }
- } else {
- if (searchCallback == null) {
- searchCallback =
- new BoundaryCallbackMessages(
- this,
- model,
+ searchCallback = new BoundaryCallbackMessages(this, model,
new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
@Override
public void onLoading() {
- tvNoEmail.setVisibility(View.GONE);
pbWait.setVisibility(View.VISIBLE);
}
@Override
public void onLoaded() {
pbWait.setVisibility(View.GONE);
- if (messages.getValue() == null || messages.getValue().size() == 0) {
- tvNoEmail.setVisibility(View.VISIBLE);
- }
}
@Override
@@ -1254,119 +1099,156 @@ public class FragmentMessages extends FragmentEx {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner())
.setMessage(Helper.formatThrowable(ex))
- .setPositiveButton(android.R.string.cancel, null)
- .create()
- .show();
+ .setPositiveButton(android.R.string.cancel, null).create().show();
}
}
});
- }
- PagedList.Config config =
- new PagedList.Config.Builder()
- .setPageSize(LOCAL_PAGE_SIZE)
- .setInitialLoadSizeHint(LOCAL_PAGE_SIZE)
- .setPrefetchDistance(REMOTE_PAGE_SIZE)
- .build();
- LivePagedListBuilder builder =
- new LivePagedListBuilder<>(
- db.message().pagedFolder(folder, folderType, "time", true, false), config);
- builder.setBoundaryCallback(searchCallback);
- messages = builder.build();
- }
+ }
- messages.observe(
- getViewLifecycleOwner(),
- new Observer>() {
- @Override
- public void onChanged(@Nullable PagedList messages) {
- if (messages == null
- || (viewType == AdapterMessage.ViewType.THREAD && messages.size() == 0)) {
- finish();
- return;
- }
+ PagedList.Config config = new PagedList.Config.Builder().setPageSize(LOCAL_PAGE_SIZE)
+ .setInitialLoadSizeHint(LOCAL_PAGE_SIZE).setPrefetchDistance(REMOTE_PAGE_SIZE)
+ .build();
+ LivePagedListBuilder builder = new LivePagedListBuilder<>(
+ db.message().pagedFolder(folder, folderType, sort, false, debug), config);
+ if (browse) {
+ builder.setBoundaryCallback(searchCallback);
+ }
+ messages = builder.build();
- if (viewType == AdapterMessage.ViewType.THREAD) {
- if (autoExpand) {
- autoExpand = false;
+ break;
+ case THREAD:
+ messages = new LivePagedListBuilder<>(
+ db.message().pagedThread(account, folder, thread, sort, debug), LOCAL_PAGE_SIZE)
+ .build();
+ break;
+ }
+ } else {
+ if (searchCallback == null) {
+ searchCallback = new BoundaryCallbackMessages(this, model,
+ new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
+ @Override
+ public void onLoading() {
+ tvNoEmail.setVisibility(View.GONE);
+ pbWait.setVisibility(View.VISIBLE);
+ }
- int unseen = 0;
- TupleMessageEx single = null;
- TupleMessageEx see = null;
- for (int i = 0; i < messages.size(); i++) {
- TupleMessageEx message = messages.get(i);
- if (!EntityFolder.ARCHIVE.equals(message.folderType)
- && !EntityFolder.SENT.equals(message.folderType)
- && !EntityFolder.OUTBOX.equals(message.folderType)) {
- autoCount++;
- single = message;
- if (!message.ui_seen) {
- unseen++;
- see = message;
- }
- }
+ @Override
+ public void onLoaded() {
+ pbWait.setVisibility(View.GONE);
+ if (messages.getValue() == null || messages.getValue().size() == 0) {
+ tvNoEmail.setVisibility(View.VISIBLE);
}
+ }
- // Auto expand when:
- // - single, non archived/sent message
- // - one unread, non archived/sent message in conversation
- // - sole message
-
- TupleMessageEx expand = null;
- if (autoCount == 1) {
- expand = single;
- } else if (unseen == 1) {
- expand = see;
- } else if (messages.size() == 1) {
- expand = messages.get(0);
- }
- if (expand != null) {
- expanded.add(expand.id);
- handleExpand(expand.id);
- }
- } else if (autoCount > 0) {
- int count = 0;
- for (int i = 0; i < messages.size(); i++) {
- TupleMessageEx message = messages.get(i);
- if (!EntityFolder.ARCHIVE.equals(message.folderType)
- && !EntityFolder.SENT.equals(message.folderType)
- && !EntityFolder.OUTBOX.equals(message.folderType)) {
- count++;
- }
+ @Override
+ public void onError(Context context, Throwable ex) {
+ if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
+ new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner())
+ .setMessage(Helper.formatThrowable(ex))
+ .setPositiveButton(android.R.string.cancel, null).create().show();
}
+ }
+ });
+ }
+ PagedList.Config config = new PagedList.Config.Builder().setPageSize(LOCAL_PAGE_SIZE)
+ .setInitialLoadSizeHint(LOCAL_PAGE_SIZE).setPrefetchDistance(REMOTE_PAGE_SIZE).build();
+ LivePagedListBuilder builder = new LivePagedListBuilder<>(
+ db.message().pagedFolder(folder, folderType, "time", true, false), config);
+ builder.setBoundaryCallback(searchCallback);
+ messages = builder.build();
+ }
- // Auto close when:
- // - no more non archived/sent messages
+ messages.observe(getViewLifecycleOwner(), new Observer>() {
+ @Override
+ public void onChanged(@Nullable PagedList messages) {
+ if (messages == null
+ || (viewType == AdapterMessage.ViewType.THREAD && messages.size() == 0)) {
+ finish();
+ return;
+ }
- if (count == 0) {
- finish();
+ if (viewType == AdapterMessage.ViewType.THREAD) {
+ if (autoExpand) {
+ autoExpand = false;
+
+ int unseen = 0;
+ TupleMessageEx single = null;
+ TupleMessageEx see = null;
+ for (int i = 0; i < messages.size(); i++) {
+ TupleMessageEx message = messages.get(i);
+ if (!EntityFolder.ARCHIVE.equals(message.folderType)
+ && !EntityFolder.SENT.equals(message.folderType)
+ && !EntityFolder.OUTBOX.equals(message.folderType)) {
+ autoCount++;
+ single = message;
+ if (!message.ui_seen) {
+ unseen++;
+ see = message;
}
}
- } else {
- ViewModelMessages model =
- ViewModelProviders.of(getActivity()).get(ViewModelMessages.class);
- model.setMessages(messages);
}
- Log.i(Helper.TAG, "Submit messages=" + messages.size());
- adapter.submitList(messages);
-
- boolean searching = (searchCallback != null && searchCallback.isSearching());
-
- if (!searching) {
- pbWait.setVisibility(View.GONE);
+ // Auto expand when:
+ // - single, non archived/sent message
+ // - one unread, non archived/sent message in conversation
+ // - sole message
+
+ TupleMessageEx expand = null;
+ if (autoCount == 1) {
+ expand = single;
+ } else if (unseen == 1) {
+ expand = see;
+ } else if (messages.size() == 1) {
+ expand = messages.get(0);
}
- grpReady.setVisibility(View.VISIBLE);
-
- if (messages.size() == 0) {
- if (searchCallback == null) {
- tvNoEmail.setVisibility(View.VISIBLE);
+ if (expand != null) {
+ expanded.add(expand.id);
+ handleExpand(expand.id);
+ }
+ } else if (autoCount > 0) {
+ int count = 0;
+ for (int i = 0; i < messages.size(); i++) {
+ TupleMessageEx message = messages.get(i);
+ if (!EntityFolder.ARCHIVE.equals(message.folderType)
+ && !EntityFolder.SENT.equals(message.folderType)
+ && !EntityFolder.OUTBOX.equals(message.folderType)) {
+ count++;
}
- rvMessage.setVisibility(View.GONE);
- } else {
- tvNoEmail.setVisibility(View.GONE);
- rvMessage.setVisibility(View.VISIBLE);
+ }
+
+ // Auto close when:
+ // - no more non archived/sent messages
+
+ if (count == 0) {
+ finish();
}
}
+ } else {
+ ViewModelMessages model =
+ ViewModelProviders.of(getActivity()).get(ViewModelMessages.class);
+ model.setMessages(messages);
+ }
+
+ Log.i(Helper.TAG, "Submit messages=" + messages.size());
+ adapter.submitList(messages);
+
+ boolean searching = (searchCallback != null && searchCallback.isSearching());
+
+ if (!searching) {
+ pbWait.setVisibility(View.GONE);
+ }
+ grpReady.setVisibility(View.VISIBLE);
+
+ if (messages.size() == 0) {
+ if (searchCallback == null) {
+ tvNoEmail.setVisibility(View.VISIBLE);
+ }
+ rvMessage.setVisibility(View.GONE);
+ } else {
+ tvNoEmail.setVisibility(View.GONE);
+ rvMessage.setVisibility(View.VISIBLE);
+ }
+ }
});
}
diff --git a/app/src/main/java/org/dystopia/email/ServiceSynchronize.java b/app/src/main/java/org/dystopia/email/ServiceSynchronize.java
index 36eca16e..65d17f02 100644
--- a/app/src/main/java/org/dystopia/email/ServiceSynchronize.java
+++ b/app/src/main/java/org/dystopia/email/ServiceSynchronize.java
@@ -1,27 +1,24 @@
package org.dystopia.email;
/*
- This file is part of FairEmail.
-
- FairEmail is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- FairEmail is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with FairEmail. If not, see .
-
- Copyright 2018, Marcel Bokhorst (M66B)
- Copyright 2018, Distopico (dystopia project) and contributors
-*/
+ * This file is part of FairEmail.
+ *
+ * FairEmail is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * FairEmail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with FairEmail. If not,
+ * see .
+ *
+ * Copyright 2018, Marcel Bokhorst (M66B) Copyright 2018, Distopico (dystopia project)
+ * and contributors
+ */
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
-
import android.Manifest;
import android.app.AlarmManager;
import android.app.Notification;
@@ -160,111 +157,101 @@ public class ServiceSynchronize extends LifecycleService {
DB db = DB.getInstance(this);
- db.account()
- .liveStats()
- .observe(
- this,
- new Observer() {
- @Override
- public void onChanged(@Nullable TupleAccountStats stats) {
- NotificationManager nm = getSystemService(NotificationManager.class);
- nm.notify(NOTIFICATION_SYNCHRONIZE, getNotificationService(stats).build());
- }
- });
+ db.account().liveStats().observe(this, new Observer() {
+ @Override
+ public void onChanged(@Nullable TupleAccountStats stats) {
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ nm.notify(NOTIFICATION_SYNCHRONIZE, getNotificationService(stats).build());
+ }
+ });
- db.message()
- .liveUnseenUnified()
- .observe(
- this,
- new Observer>() {
- private LongSparseArray> notifying = new LongSparseArray<>();
- private LongSparseArray accounts = new LongSparseArray<>();
+ db.message().liveUnseenUnified().observe(this, new Observer>() {
+ private LongSparseArray> notifying = new LongSparseArray<>();
+ private LongSparseArray accounts = new LongSparseArray<>();
- @Override
- public void onChanged(List messages) {
- NotificationManager nm = getSystemService(NotificationManager.class);
- LongSparseArray> messagesByAccount =
- new LongSparseArray<>();
- LongSparseArray> removed = notifying.clone();
+ @Override
+ public void onChanged(List messages) {
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ LongSparseArray> messagesByAccount = new LongSparseArray<>();
+ LongSparseArray> removed = notifying.clone();
- // Update unseen for all account
- setWidgetUnseen(messages);
+ // Update unseen for all account
+ setWidgetUnseen(messages);
- if (messages.size() == 0) {
- nm.cancelAll();
- return;
- }
+ if (messages.size() == 0) {
+ nm.cancelAll();
+ return;
+ }
- // Organize messages per account
- for (TupleNotification message : messages) {
+ // Organize messages per account
+ for (TupleNotification message : messages) {
- Long accountKey = message.account;
- List msgList = new ArrayList<>();
+ Long accountKey = message.account;
+ List msgList = new ArrayList<>();
- if (messagesByAccount.indexOfKey(accountKey) != -1) {
- msgList = messagesByAccount.get(accountKey);
- }
+ if (messagesByAccount.indexOfKey(accountKey) != -1) {
+ msgList = messagesByAccount.get(accountKey);
+ }
- if (accounts.indexOfKey(accountKey) == -1) {
- accounts.put(
- accountKey,
- new Pair(message.accountName, message.accountColor));
- }
+ if (accounts.indexOfKey(accountKey) == -1) {
+ accounts.put(accountKey,
+ new Pair(message.accountName, message.accountColor));
+ }
- msgList.add(message);
- messagesByAccount.put(accountKey, msgList);
- }
+ msgList.add(message);
+ messagesByAccount.put(accountKey, msgList);
+ }
- // Set and group notification per account
- for (int i = 0; i < messagesByAccount.size(); i++) {
- Long accountId = messagesByAccount.keyAt(i);
- List messagesAccount = messagesByAccount.get(accountId);
- List notifications =
- getNotificationUnseen(messagesAccount, accounts.get(accountId));
- List all = new ArrayList<>();
- List added = new ArrayList<>();
- List toRemove = new ArrayList<>();
- String tag = "unseen-" + accountId;
-
- if (notifying.indexOfKey(accountId) != -1) {
- toRemove = notifying.get(accountId);
- }
+ // Set and group notification per account
+ for (int i = 0; i < messagesByAccount.size(); i++) {
+ Long accountId = messagesByAccount.keyAt(i);
+ List messagesAccount = messagesByAccount.get(accountId);
+ List notifications =
+ getNotificationUnseen(messagesAccount, accounts.get(accountId));
+ List all = new ArrayList<>();
+ List added = new ArrayList<>();
+ List toRemove = new ArrayList<>();
+ String tag = "unseen-" + accountId;
+
+ if (notifying.indexOfKey(accountId) != -1) {
+ toRemove = notifying.get(accountId);
+ }
- for (Notification notification : notifications) {
- Integer id = (int) notification.extras.getLong("id", 0);
+ for (Notification notification : notifications) {
+ Integer id = (int) notification.extras.getLong("id", 0);
- all.add(id);
- if (toRemove.contains(id)) {
- toRemove.remove(id);
- } else if (id > 0) {
- added.add(id);
- }
- }
+ all.add(id);
+ if (toRemove.contains(id)) {
+ toRemove.remove(id);
+ } else if (id > 0) {
+ added.add(id);
+ }
+ }
- for (Notification notification : notifications) {
- Integer id = (int) notification.extras.getLong("id", 0);
+ for (Notification notification : notifications) {
+ Integer id = (int) notification.extras.getLong("id", 0);
- if ((id == 0 && added.size() > 0) || added.contains(id)) {
- nm.notify(tag, id, notification);
- }
- }
+ if ((id == 0 && added.size() > 0) || added.contains(id)) {
+ nm.notify(tag, id, notification);
+ }
+ }
- removed.put(accountId, toRemove);
- notifying.put(accountId, all);
- }
+ removed.put(accountId, toRemove);
+ notifying.put(accountId, all);
+ }
- // Cancel old per account
- for (int i = 0; i < removed.size(); i++) {
- Long accountId = removed.keyAt(i);
- List notifyRemove = removed.get(accountId);
- String tag = "unseen-" + accountId;
+ // Cancel old per account
+ for (int i = 0; i < removed.size(); i++) {
+ Long accountId = removed.keyAt(i);
+ List notifyRemove = removed.get(accountId);
+ String tag = "unseen-" + accountId;
- for (Integer id : notifyRemove) {
- nm.cancel(tag, id);
- }
- }
- }
- });
+ for (Integer id : notifyRemove) {
+ nm.cancel(tag, id);
+ }
+ }
+ }
+ });
}
@Override
@@ -310,8 +297,7 @@ public class ServiceSynchronize extends LifecycleService {
return null;
}
}.load(this, new Bundle());
- } else if (action.startsWith("seen:")
- || action.startsWith("trash:")
+ } else if (action.startsWith("seen:") || action.startsWith("trash:")
|| action.startsWith("ignored:")) {
Bundle args = new Bundle();
args.putLong("id", Long.parseLong(action.split(":")[1]));
@@ -370,41 +356,28 @@ public class ServiceSynchronize extends LifecycleService {
// Build pending intent
Intent intent = new Intent(this, ActivityView.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pi =
- PendingIntent.getActivity(
- this, ActivityView.REQUEST_UNIFIED, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent pi = PendingIntent.getActivity(this, ActivityView.REQUEST_UNIFIED, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
// Build notification
Notification.Builder builder = Helper.getNotificationBuilder(this, "service");
- builder
- .setSmallIcon(R.drawable.baseline_compare_arrows_white_24)
- .setContentTitle(
- getResources()
- .getQuantityString(
- R.plurals.title_notification_synchronizing, stats.accounts, stats.accounts))
- .setContentIntent(pi)
- .setAutoCancel(false)
- .setShowWhen(false)
- .setPriority(Notification.PRIORITY_MIN)
- .setCategory(Notification.CATEGORY_STATUS)
+ builder.setSmallIcon(R.drawable.baseline_compare_arrows_white_24)
+ .setContentTitle(getResources().getQuantityString(
+ R.plurals.title_notification_synchronizing, stats.accounts, stats.accounts))
+ .setContentIntent(pi).setAutoCancel(false).setShowWhen(false)
+ .setPriority(Notification.PRIORITY_MIN).setCategory(Notification.CATEGORY_STATUS)
.setVisibility(Notification.VISIBILITY_SECRET);
if (stats.operations > 0) {
- builder.setStyle(
- new Notification.BigTextStyle()
- .setSummaryText(
- getResources()
- .getQuantityString(
- R.plurals.title_notification_operations,
- stats.operations,
- stats.operations)));
+ builder
+ .setStyle(new Notification.BigTextStyle().setSummaryText(getResources().getQuantityString(
+ R.plurals.title_notification_operations, stats.operations, stats.operations)));
}
if (stats.unsent > 0) {
- builder.setContentText(
- getResources()
- .getQuantityString(R.plurals.title_notification_unsent, stats.unsent, stats.unsent));
+ builder.setContentText(getResources().getQuantityString(R.plurals.title_notification_unsent,
+ stats.unsent, stats.unsent));
}
lastStats = stats;
@@ -428,8 +401,7 @@ public class ServiceSynchronize extends LifecycleService {
* @return Integer
*/
private Integer getNotificationColor(Integer accountColor) {
- return accountColor != null
- ? accountColor
+ return accountColor != null ? accountColor
: ContextCompat.getColor(getBaseContext(), R.color.colorPrimary);
}
@@ -451,16 +423,15 @@ public class ServiceSynchronize extends LifecycleService {
* @param size - number of new messages
* @return Notification.Builder
*/
- private Notification.Builder getNotificationPublic(
- String accountName, Integer accountColor, Integer size) {
+ private Notification.Builder getNotificationPublic(String accountName, Integer accountColor,
+ Integer size) {
String channelId = "notification";
// Build pending intent
Intent view = new Intent(this, ActivityView.class);
view.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent piView =
- PendingIntent.getActivity(
- this, ActivityView.REQUEST_UNIFIED, view, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent piView = PendingIntent.getActivity(this, ActivityView.REQUEST_UNIFIED, view,
+ PendingIntent.FLAG_UPDATE_CURRENT);
Intent clear = new Intent(this, ServiceSynchronize.class);
clear.setAction("clear");
@@ -472,18 +443,10 @@ public class ServiceSynchronize extends LifecycleService {
// Public notification
Notification.Builder pbuilder = Helper.getNotificationBuilder(this, channelId);
- pbuilder
- .setSmallIcon(R.drawable.ic_mail_icon)
- .setContentTitle(summaryText)
- .setContentText(accountName)
- .setContentIntent(piView)
- .setNumber(size)
- .setShowWhen(true)
- .setColor(accountColor)
- .setDeleteIntent(piClear)
- .setPriority(Notification.PRIORITY_DEFAULT)
- .setCategory(Notification.CATEGORY_STATUS)
- .setVisibility(Notification.VISIBILITY_PUBLIC);
+ pbuilder.setSmallIcon(R.drawable.ic_mail_icon).setContentTitle(summaryText)
+ .setContentText(accountName).setContentIntent(piView).setNumber(size).setShowWhen(true)
+ .setColor(accountColor).setDeleteIntent(piClear).setPriority(Notification.PRIORITY_DEFAULT)
+ .setCategory(Notification.CATEGORY_STATUS).setVisibility(Notification.VISIBILITY_PUBLIC);
return pbuilder;
}
@@ -496,8 +459,8 @@ public class ServiceSynchronize extends LifecycleService {
* @param messages - account messages
* @return Notification.Builder
*/
- private Notification.Builder getNotificationGroup(
- String accountName, Integer accountColor, List messages) {
+ private Notification.Builder getNotificationGroup(String accountName, Integer accountColor,
+ List messages) {
// https://developer.android.com/training/notify-user/group
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Integer groupColor = getNotificationColor(accountColor);
@@ -508,9 +471,8 @@ public class ServiceSynchronize extends LifecycleService {
// Build pending intent
Intent view = new Intent(this, ActivityView.class);
view.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent piView =
- PendingIntent.getActivity(
- this, ActivityView.REQUEST_UNIFIED, view, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent piView = PendingIntent.getActivity(this, ActivityView.REQUEST_UNIFIED, view,
+ PendingIntent.FLAG_UPDATE_CURRENT);
Intent clear = new Intent(this, ServiceSynchronize.class);
clear.setAction("clear");
@@ -524,20 +486,11 @@ public class ServiceSynchronize extends LifecycleService {
Notification.Builder pbuilder = getNotificationPublic(accountName, accountColor, size);
Notification.Builder gbuilder = Helper.getNotificationBuilder(this, channelId);
- gbuilder
- .setSmallIcon(R.drawable.ic_mail_icon)
- .setContentTitle(summaryText)
- .setContentIntent(piView)
- .setNumber(messages.size())
- .setShowWhen(true)
- .setColor(groupColor)
- .setDeleteIntent(piClear)
- .setPriority(Notification.PRIORITY_DEFAULT)
- .setCategory(Notification.CATEGORY_STATUS)
- .setVisibility(Notification.VISIBILITY_PRIVATE)
- .setGroup(groupKey)
- .setGroupSummary(true)
- .setPublicVersion(pbuilder.build());
+ gbuilder.setSmallIcon(R.drawable.ic_mail_icon).setContentTitle(summaryText)
+ .setContentIntent(piView).setNumber(messages.size()).setShowWhen(true).setColor(groupColor)
+ .setDeleteIntent(piClear).setPriority(Notification.PRIORITY_DEFAULT)
+ .setCategory(Notification.CATEGORY_STATUS).setVisibility(Notification.VISIBILITY_PRIVATE)
+ .setGroup(groupKey).setGroupSummary(true).setPublicVersion(pbuilder.build());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
gbuilder.setSound(null);
@@ -555,8 +508,7 @@ public class ServiceSynchronize extends LifecycleService {
SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT);
StringBuilder sb = new StringBuilder();
for (EntityMessage message : messages) {
- sb.append("")
- .append(MessageHelper.getFormattedAddresses(message.from, false))
+ sb.append("").append(MessageHelper.getFormattedAddresses(message.from, false))
.append("");
if (!TextUtils.isEmpty(message.subject)) {
@@ -567,10 +519,8 @@ public class ServiceSynchronize extends LifecycleService {
sb.append("
");
}
- Notification.BigTextStyle gstyle =
- new Notification.BigTextStyle()
- .bigText(Html.fromHtml(sb.toString()))
- .setSummaryText(accountName);
+ Notification.BigTextStyle gstyle = new Notification.BigTextStyle()
+ .bigText(Html.fromHtml(sb.toString())).setSummaryText(accountName);
gbuilder.setStyle(gstyle);
@@ -612,9 +562,8 @@ public class ServiceSynchronize extends LifecycleService {
thread.setAction("thread:" + message.thread);
thread.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
thread.putExtra("account", message.account);
- PendingIntent piContent =
- PendingIntent.getActivity(
- this, ActivityView.REQUEST_THREAD, thread, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent piContent = PendingIntent.getActivity(this, ActivityView.REQUEST_THREAD, thread,
+ PendingIntent.FLAG_UPDATE_CURRENT);
Intent ignored = new Intent(this, ServiceSynchronize.class);
ignored.setAction("ignored:" + message.id);
@@ -631,37 +580,24 @@ public class ServiceSynchronize extends LifecycleService {
PendingIntent piTrash =
PendingIntent.getService(this, PI_TRASH, trash, PendingIntent.FLAG_UPDATE_CURRENT);
- Notification.Action.Builder actionSeen =
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.baseline_visibility_24),
- getString(R.string.title_seen),
- piSeen);
+ Notification.Action.Builder actionSeen = new Notification.Action.Builder(
+ Icon.createWithResource(this, R.drawable.baseline_visibility_24),
+ getString(R.string.title_seen), piSeen);
- Notification.Action.Builder actionTrash =
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.baseline_delete_24),
- getString(R.string.title_trash),
- piTrash);
+ Notification.Action.Builder actionTrash = new Notification.Action.Builder(
+ Icon.createWithResource(this, R.drawable.baseline_delete_24),
+ getString(R.string.title_trash), piTrash);
Notification.Builder mbuilder = Helper.getNotificationBuilder(this, channelId);
Notification.InboxStyle mstyle = new Notification.InboxStyle();
- mbuilder
- .addExtras(mArgs)
- .setSmallIcon(R.drawable.ic_mail_icon)
+ mbuilder.addExtras(mArgs).setSmallIcon(R.drawable.ic_mail_icon)
.setContentTitle(MessageHelper.getFormattedAddresses(message.from, true))
- .setContentIntent(piContent)
- .setDeleteIntent(piDelete)
- .setSound(uri)
- .setColor(groupColor)
+ .setContentIntent(piContent).setDeleteIntent(piDelete).setSound(uri).setColor(groupColor)
.setWhen(message.sent == null ? message.received : message.sent)
- .setPriority(Notification.PRIORITY_DEFAULT)
- .setCategory(Notification.CATEGORY_STATUS)
- .setVisibility(Notification.VISIBILITY_PRIVATE)
- .setGroup(groupKey)
- .setGroupSummary(false)
- .addAction(actionSeen.build())
- .addAction(actionTrash.build());
+ .setPriority(Notification.PRIORITY_DEFAULT).setCategory(Notification.CATEGORY_STATUS)
+ .setVisibility(Notification.VISIBILITY_PRIVATE).setGroup(groupKey).setGroupSummary(false)
+ .addAction(actionSeen.build()).addAction(actionTrash.build());
if (!TextUtils.isEmpty(message.subject)) {
mbuilder.setContentText(message.subject);
@@ -672,8 +608,7 @@ public class ServiceSynchronize extends LifecycleService {
mbuilder.setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN);
}
- mstyle
- .setBigContentTitle(MessageHelper.getFormattedAddresses(message.from, false))
+ mstyle.setBigContentTitle(MessageHelper.getFormattedAddresses(message.from, false))
.setSummaryText(accountName);
mbuilder.setStyle(mstyle);
@@ -686,24 +621,17 @@ public class ServiceSynchronize extends LifecycleService {
// Build pending intent
Intent intent = new Intent(this, ActivityView.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pi =
- PendingIntent.getActivity(
- this, ActivityView.REQUEST_ERROR, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent pi = PendingIntent.getActivity(this, ActivityView.REQUEST_ERROR, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
// Build notification
Notification.Builder builder = Helper.getNotificationBuilder(this, "error");
- builder
- .setSmallIcon(android.R.drawable.stat_notify_error)
+ builder.setSmallIcon(android.R.drawable.stat_notify_error)
.setContentTitle(getString(R.string.title_notification_failed, action))
- .setContentText(Helper.formatThrowable(ex))
- .setContentIntent(pi)
- .setAutoCancel(false)
- .setOnlyAlertOnce(true)
- .setShowWhen(true)
- .setPriority(Notification.PRIORITY_MAX)
- .setCategory(Notification.CATEGORY_ERROR)
- .setVisibility(Notification.VISIBILITY_SECRET);
+ .setContentText(Helper.formatThrowable(ex)).setContentIntent(pi).setAutoCancel(false)
+ .setOnlyAlertOnce(true).setShowWhen(true).setPriority(Notification.PRIORITY_MAX)
+ .setCategory(Notification.CATEGORY_ERROR).setVisibility(Notification.VISIBILITY_SECRET);
builder.setStyle(new Notification.BigTextStyle().bigText(ex.toString()));
@@ -739,12 +667,9 @@ public class ServiceSynchronize extends LifecycleService {
nm.notify(action, 1, getNotificationError(action, ex).build());
}
- if (BuildConfig.DEBUG
- && !(ex instanceof SendFailedException)
- && !(ex instanceof MailConnectException)
- && !(ex instanceof FolderClosedException)
- && !(ex instanceof IllegalStateException)
- && !(ex instanceof AuthenticationFailedException)
+ if (BuildConfig.DEBUG && !(ex instanceof SendFailedException)
+ && !(ex instanceof MailConnectException) && !(ex instanceof FolderClosedException)
+ && !(ex instanceof IllegalStateException) && !(ex instanceof AuthenticationFailedException)
&& // Also: Too many simultaneous connections
!(ex instanceof StoreClosedException)
&& !(ex instanceof MessagingException && ex.getCause() instanceof UnknownHostException)
@@ -761,10 +686,8 @@ public class ServiceSynchronize extends LifecycleService {
private void monitorAccount(final EntityAccount account, final ServiceState state)
throws NoSuchProviderException {
final PowerManager pm = getSystemService(PowerManager.class);
- final PowerManager.WakeLock wl0 =
- pm.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK,
- BuildConfig.APPLICATION_ID + ":account." + account.id + ".monitor");
+ final PowerManager.WakeLock wl0 = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ BuildConfig.APPLICATION_ID + ":account." + account.id + ".monitor");
try {
wl0.acquire();
@@ -793,96 +716,89 @@ public class ServiceSynchronize extends LifecycleService {
List idlers = new ArrayList<>();
try {
// Listen for store events
- istore.addStoreListener(
- new StoreListener() {
- PowerManager.WakeLock wl =
- pm.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK,
- BuildConfig.APPLICATION_ID + ":account." + account.id + ".store");
+ istore.addStoreListener(new StoreListener() {
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ BuildConfig.APPLICATION_ID + ":account." + account.id + ".store");
- @Override
- public void notification(StoreEvent e) {
- try {
- wl.acquire();
- Log.i(Helper.TAG, account.name + " event: " + e.getMessage());
- db.account().setAccountError(account.id, e.getMessage());
- state.thread.interrupt();
- yieldWakelock();
- } finally {
- wl.release();
- }
- }
- });
+ @Override
+ public void notification(StoreEvent e) {
+ try {
+ wl.acquire();
+ Log.i(Helper.TAG, account.name + " event: " + e.getMessage());
+ db.account().setAccountError(account.id, e.getMessage());
+ state.thread.interrupt();
+ yieldWakelock();
+ } finally {
+ wl.release();
+ }
+ }
+ });
// Listen for folder events
- istore.addFolderListener(
- new FolderAdapter() {
- PowerManager.WakeLock wl =
- pm.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK,
- BuildConfig.APPLICATION_ID + ":account." + account.id + ".folder");
+ istore.addFolderListener(new FolderAdapter() {
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ BuildConfig.APPLICATION_ID + ":account." + account.id + ".folder");
- @Override
- public void folderCreated(FolderEvent e) {
- try {
- wl.acquire();
- Log.i(Helper.TAG, "Folder created=" + e.getFolder().getFullName());
- state.thread.interrupt();
- yieldWakelock();
- } finally {
- wl.release();
- }
- }
+ @Override
+ public void folderCreated(FolderEvent e) {
+ try {
+ wl.acquire();
+ Log.i(Helper.TAG, "Folder created=" + e.getFolder().getFullName());
+ state.thread.interrupt();
+ yieldWakelock();
+ } finally {
+ wl.release();
+ }
+ }
- @Override
- public void folderRenamed(FolderEvent e) {
- try {
- wl.acquire();
- Log.i(Helper.TAG, "Folder renamed=" + e.getFolder());
+ @Override
+ public void folderRenamed(FolderEvent e) {
+ try {
+ wl.acquire();
+ Log.i(Helper.TAG, "Folder renamed=" + e.getFolder());
- String old = e.getFolder().getFullName();
- String name = e.getNewFolder().getFullName();
- int count = db.folder().renameFolder(account.id, old, name);
- Log.i(Helper.TAG, "Renamed to " + name + " count=" + count);
+ String old = e.getFolder().getFullName();
+ String name = e.getNewFolder().getFullName();
+ int count = db.folder().renameFolder(account.id, old, name);
+ Log.i(Helper.TAG, "Renamed to " + name + " count=" + count);
- state.thread.interrupt();
- yieldWakelock();
- } finally {
- wl.release();
- }
- }
+ state.thread.interrupt();
+ yieldWakelock();
+ } finally {
+ wl.release();
+ }
+ }
- @Override
- public void folderDeleted(FolderEvent e) {
- try {
- wl.acquire();
- Log.i(Helper.TAG, "Folder deleted=" + e.getFolder().getFullName());
- state.thread.interrupt();
- yieldWakelock();
- } finally {
- wl.release();
- }
- }
- });
+ @Override
+ public void folderDeleted(FolderEvent e) {
+ try {
+ wl.acquire();
+ Log.i(Helper.TAG, "Folder deleted=" + e.getFolder().getFullName());
+ state.thread.interrupt();
+ yieldWakelock();
+ } finally {
+ wl.release();
+ }
+ }
+ });
// Listen for connection events
- istore.addConnectionListener(
- new ConnectionAdapter() {
- @Override
- public void opened(ConnectionEvent e) {
- Log.i(Helper.TAG, account.name + " opened");
- }
+ istore.addConnectionListener(new ConnectionAdapter() {
+ @Override
+ public void opened(ConnectionEvent e) {
+ Log.i(Helper.TAG, account.name + " opened");
+ }
- @Override
- public void disconnected(ConnectionEvent e) {
- Log.e(Helper.TAG, account.name + " disconnected event");
- }
+ @Override
+ public void disconnected(ConnectionEvent e) {
+ Log.e(Helper.TAG, account.name + " disconnected event");
+ }
- @Override
- public void closed(ConnectionEvent e) {
- Log.e(Helper.TAG, account.name + " closed event");
- }
- });
+ @Override
+ public void closed(ConnectionEvent e) {
+ Log.e(Helper.TAG, account.name + " closed event");
+ }
+ });
// Initiate connection
Log.i(Helper.TAG, account.name + " connect");
@@ -920,243 +836,159 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderError(folder.id, null);
// Synchronize folder
- Thread sync =
- new Thread(
- new Runnable() {
- PowerManager.WakeLock wl =
- pm.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK,
- BuildConfig.APPLICATION_ID + ":account." + account.id + ".sync");
-
- @Override
- public void run() {
+ Thread sync = new Thread(new Runnable() {
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ BuildConfig.APPLICATION_ID + ":account." + account.id + ".sync");
+
+ @Override
+ public void run() {
+ try {
+ wl.acquire();
+
+ // Process pending operations
+ processOperations(folder, isession, istore, ifolder);
+
+ // Listen for new and deleted messages
+ ifolder.addMessageCountListener(new MessageCountAdapter() {
+ @Override
+ public void messagesAdded(MessageCountEvent e) {
+ synchronized (lock) {
+ try {
+ wl.acquire();
+ Log.i(Helper.TAG, folder.name + " messages added");
+
+ FetchProfile fp = new FetchProfile();
+ fp.add(FetchProfile.Item.ENVELOPE);
+ fp.add(FetchProfile.Item.FLAGS);
+ fp.add(FetchProfile.Item.CONTENT_INFO); // body structure
+ fp.add(UIDFolder.FetchProfileItem.UID);
+ fp.add(IMAPFolder.FetchProfileItem.HEADERS);
+ fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
+ fp.add(FetchProfile.Item.SIZE);
+ fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
+ ifolder.fetch(e.getMessages(), fp);
+
+ for (Message imessage : e.getMessages()) {
+ try {
+ long id;
+ try {
+ db.beginTransaction();
+ id = synchronizeMessage(ServiceSynchronize.this, folder, ifolder,
+ (IMAPMessage) imessage, false);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ downloadMessage(ServiceSynchronize.this, folder, ifolder,
+ (IMAPMessage) imessage, id);
+ } catch (MessageRemovedException ex) {
+ Log.w(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ } catch (IOException ex) {
+ if (ex.getCause() instanceof MessageRemovedException) {
+ Log.w(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ } else {
+ throw ex;
+ }
+ }
+ }
+ EntityOperation.process(ServiceSynchronize.this); // download small
+ // attachments
+ } catch (Throwable ex) {
+ Log.e(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ reportError(account.name, folder.name, ex);
+
+ db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
+
+ state.thread.interrupt();
+ yieldWakelock();
+ } finally {
+ wl.release();
+ }
+ }
+ }
+
+ @Override
+ public void messagesRemoved(MessageCountEvent e) {
+ synchronized (lock) {
try {
wl.acquire();
+ Log.i(Helper.TAG, folder.name + " messages removed");
+ for (Message imessage : e.getMessages()) {
+ try {
+ long uid = ifolder.getUID(imessage);
- // Process pending operations
- processOperations(folder, isession, istore, ifolder);
-
- // Listen for new and deleted messages
- ifolder.addMessageCountListener(
- new MessageCountAdapter() {
- @Override
- public void messagesAdded(MessageCountEvent e) {
- synchronized (lock) {
- try {
- wl.acquire();
- Log.i(Helper.TAG, folder.name + " messages added");
-
- FetchProfile fp = new FetchProfile();
- fp.add(FetchProfile.Item.ENVELOPE);
- fp.add(FetchProfile.Item.FLAGS);
- fp.add(FetchProfile.Item.CONTENT_INFO); // body structure
- fp.add(UIDFolder.FetchProfileItem.UID);
- fp.add(IMAPFolder.FetchProfileItem.HEADERS);
- fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
- fp.add(FetchProfile.Item.SIZE);
- fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
- ifolder.fetch(e.getMessages(), fp);
-
- for (Message imessage : e.getMessages()) {
- try {
- long id;
- try {
- db.beginTransaction();
- id =
- synchronizeMessage(
- ServiceSynchronize.this,
- folder,
- ifolder,
- (IMAPMessage) imessage,
- false);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- downloadMessage(
- ServiceSynchronize.this,
- folder,
- ifolder,
- (IMAPMessage) imessage,
- id);
- } catch (MessageRemovedException ex) {
- Log.w(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- } catch (IOException ex) {
- if (ex.getCause() instanceof MessageRemovedException) {
- Log.w(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- } else {
- throw ex;
- }
- }
- }
- EntityOperation.process(
- ServiceSynchronize.this); // download small attachments
- } catch (Throwable ex) {
- Log.e(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- reportError(account.name, folder.name, ex);
-
- db.folder()
- .setFolderError(folder.id, Helper.formatThrowable(ex));
-
- state.thread.interrupt();
- yieldWakelock();
- } finally {
- wl.release();
- }
- }
- }
-
- @Override
- public void messagesRemoved(MessageCountEvent e) {
- synchronized (lock) {
- try {
- wl.acquire();
- Log.i(Helper.TAG, folder.name + " messages removed");
- for (Message imessage : e.getMessages()) {
- try {
- long uid = ifolder.getUID(imessage);
-
- DB db = DB.getInstance(ServiceSynchronize.this);
- int count = db.message().deleteMessage(folder.id, uid);
-
- Log.i(
- Helper.TAG, "Deleted uid=" + uid + " count=" + count);
- } catch (MessageRemovedException ex) {
- Log.w(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- }
- }
- } catch (Throwable ex) {
- Log.e(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- reportError(account.name, folder.name, ex);
-
- db.folder()
- .setFolderError(folder.id, Helper.formatThrowable(ex));
-
- state.thread.interrupt();
- } finally {
- wl.release();
- }
- }
- }
- });
-
- // Fetch e-mail
- synchronizeMessages(account, folder, ifolder, state);
-
- // Flags (like "seen") at the remote could be
- // changed while synchronizing
-
- // Listen for changed messages
- ifolder.addMessageChangedListener(
- new MessageChangedListener() {
- @Override
- public void messageChanged(MessageChangedEvent e) {
- synchronized (lock) {
- try {
- wl.acquire();
- try {
- Log.i(Helper.TAG, folder.name + " message changed");
-
- FetchProfile fp = new FetchProfile();
- fp.add(UIDFolder.FetchProfileItem.UID);
- fp.add(IMAPFolder.FetchProfileItem.FLAGS);
- ifolder.fetch(new Message[] {e.getMessage()}, fp);
-
- long id;
- try {
- db.beginTransaction();
- id =
- synchronizeMessage(
- ServiceSynchronize.this,
- folder,
- ifolder,
- (IMAPMessage) e.getMessage(),
- false);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- downloadMessage(
- ServiceSynchronize.this,
- folder,
- ifolder,
- (IMAPMessage) e.getMessage(),
- id);
- } catch (MessageRemovedException ex) {
- Log.w(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- } catch (IOException ex) {
- if (ex.getCause() instanceof MessageRemovedException) {
- Log.w(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- } else {
- throw ex;
- }
- }
- } catch (Throwable ex) {
- Log.e(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- reportError(account.name, folder.name, ex);
-
- db.folder()
- .setFolderError(folder.id, Helper.formatThrowable(ex));
-
- state.thread.interrupt();
- yieldWakelock();
- } finally {
- wl.release();
- }
- }
- }
- });
+ DB db = DB.getInstance(ServiceSynchronize.this);
+ int count = db.message().deleteMessage(folder.id, uid);
+
+ Log.i(Helper.TAG, "Deleted uid=" + uid + " count=" + count);
+ } catch (MessageRemovedException ex) {
+ Log.w(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ }
+ }
} catch (Throwable ex) {
- Log.e(
- Helper.TAG,
+ Log.e(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ reportError(account.name, folder.name, ex);
+
+ db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
+
+ state.thread.interrupt();
+ } finally {
+ wl.release();
+ }
+ }
+ }
+ });
+
+ // Fetch e-mail
+ synchronizeMessages(account, folder, ifolder, state);
+
+ // Flags (like "seen") at the remote could be
+ // changed while synchronizing
+
+ // Listen for changed messages
+ ifolder.addMessageChangedListener(new MessageChangedListener() {
+ @Override
+ public void messageChanged(MessageChangedEvent e) {
+ synchronized (lock) {
+ try {
+ wl.acquire();
+ try {
+ Log.i(Helper.TAG, folder.name + " message changed");
+
+ FetchProfile fp = new FetchProfile();
+ fp.add(UIDFolder.FetchProfileItem.UID);
+ fp.add(IMAPFolder.FetchProfileItem.FLAGS);
+ ifolder.fetch(new Message[] {e.getMessage()}, fp);
+
+ long id;
+ try {
+ db.beginTransaction();
+ id = synchronizeMessage(ServiceSynchronize.this, folder, ifolder,
+ (IMAPMessage) e.getMessage(), false);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ downloadMessage(ServiceSynchronize.this, folder, ifolder,
+ (IMAPMessage) e.getMessage(), id);
+ } catch (MessageRemovedException ex) {
+ Log.w(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ } catch (IOException ex) {
+ if (ex.getCause() instanceof MessageRemovedException) {
+ Log.w(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ } else {
+ throw ex;
+ }
+ }
+ } catch (Throwable ex) {
+ Log.e(Helper.TAG,
folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex);
@@ -1168,43 +1000,51 @@ public class ServiceSynchronize extends LifecycleService {
wl.release();
}
}
- },
- "sync." + folder.id);
+ }
+ });
+ } catch (Throwable ex) {
+ Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ reportError(account.name, folder.name, ex);
+
+ db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
+
+ state.thread.interrupt();
+ yieldWakelock();
+ } finally {
+ wl.release();
+ }
+ }
+ }, "sync." + folder.id);
sync.start();
syncs.add(sync);
// Idle folder
if (capIdle) {
- Thread idler =
- new Thread(
- new Runnable() {
- @Override
- public void run() {
- try {
- Log.i(Helper.TAG, folder.name + " start idle");
- while (state.running) {
- Log.i(Helper.TAG, folder.name + " do idle");
- ifolder.idle(false);
- // Log.i(Helper.TAG, folder.name + "
- // done idle");
- }
- } catch (FolderClosedException ignored) {
- } catch (Throwable ex) {
- Log.e(
- Helper.TAG,
- folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
- reportError(account.name, folder.name, ex);
+ Thread idler = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Log.i(Helper.TAG, folder.name + " start idle");
+ while (state.running) {
+ Log.i(Helper.TAG, folder.name + " do idle");
+ ifolder.idle(false);
+ // Log.i(Helper.TAG, folder.name + "
+ // done idle");
+ }
+ } catch (FolderClosedException ignored) {
+ } catch (Throwable ex) {
+ Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ reportError(account.name, folder.name, ex);
- db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
+ db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
- state.thread.interrupt();
- yieldWakelock();
- } finally {
- Log.i(Helper.TAG, folder.name + " end idle");
- }
- }
- },
- "idler." + folder.id);
+ state.thread.interrupt();
+ yieldWakelock();
+ } finally {
+ Log.i(Helper.TAG, folder.name + " end idle");
+ }
+ }
+ }, "idler." + folder.id);
idler.start();
idlers.add(idler);
}
@@ -1214,104 +1054,93 @@ public class ServiceSynchronize extends LifecycleService {
backoff = CONNECT_BACKOFF_START;
// Process folder actions
- BroadcastReceiver processFolder =
- new BroadcastReceiver() {
+ BroadcastReceiver processFolder = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, final Intent intent) {
+ executor.submit(new Runnable() {
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ BuildConfig.APPLICATION_ID + ":account." + account.id + ".process");
+
@Override
- public void onReceive(Context context, final Intent intent) {
- executor.submit(
- new Runnable() {
- PowerManager.WakeLock wl =
- pm.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK,
- BuildConfig.APPLICATION_ID + ":account." + account.id + ".process");
-
- @Override
- public void run() {
- long fid = intent.getLongExtra("folder", -1);
- try {
- wl.acquire();
- Log.i(Helper.TAG, "Process folder=" + fid + " intent=" + intent);
-
- // Get folder
- EntityFolder folder = null;
- IMAPFolder ifolder = null;
- for (EntityFolder f : folders.keySet()) {
- if (f.id == fid) {
- folder = f;
- ifolder = folders.get(f);
- break;
- }
- }
+ public void run() {
+ long fid = intent.getLongExtra("folder", -1);
+ try {
+ wl.acquire();
+ Log.i(Helper.TAG, "Process folder=" + fid + " intent=" + intent);
+
+ // Get folder
+ EntityFolder folder = null;
+ IMAPFolder ifolder = null;
+ for (EntityFolder f : folders.keySet()) {
+ if (f.id == fid) {
+ folder = f;
+ ifolder = folders.get(f);
+ break;
+ }
+ }
- final boolean shouldClose = (folder == null);
+ final boolean shouldClose = (folder == null);
- try {
- if (folder == null) {
- folder = db.folder().getFolder(fid);
- }
+ try {
+ if (folder == null) {
+ folder = db.folder().getFolder(fid);
+ }
- Log.i(
- Helper.TAG,
- folder.name + " run " + (shouldClose ? "offline" : "online"));
+ Log.i(Helper.TAG,
+ folder.name + " run " + (shouldClose ? "offline" : "online"));
- if (ifolder == null) {
- // Prevent unnecessary folder
- // connections
- if (ACTION_PROCESS_OPERATIONS.equals(intent.getAction())) {
- if (db.operation().getOperationCount(fid) == 0) {
- return;
- }
- }
+ if (ifolder == null) {
+ // Prevent unnecessary folder
+ // connections
+ if (ACTION_PROCESS_OPERATIONS.equals(intent.getAction())) {
+ if (db.operation().getOperationCount(fid) == 0) {
+ return;
+ }
+ }
- db.folder().setFolderState(folder.id, "connecting");
+ db.folder().setFolderState(folder.id, "connecting");
- ifolder = (IMAPFolder) istore.getFolder(folder.name);
- ifolder.open(Folder.READ_WRITE);
+ ifolder = (IMAPFolder) istore.getFolder(folder.name);
+ ifolder.open(Folder.READ_WRITE);
- db.folder().setFolderState(folder.id, "connected");
- db.folder().setFolderError(folder.id, null);
- }
+ db.folder().setFolderState(folder.id, "connected");
+ db.folder().setFolderError(folder.id, null);
+ }
- if (ACTION_PROCESS_OPERATIONS.equals(intent.getAction())) {
- processOperations(folder, isession, istore, ifolder);
- } else if (ACTION_SYNCHRONIZE_FOLDER.equals(intent.getAction())) {
- processOperations(folder, isession, istore, ifolder);
- synchronizeMessages(account, folder, ifolder, state);
- }
+ if (ACTION_PROCESS_OPERATIONS.equals(intent.getAction())) {
+ processOperations(folder, isession, istore, ifolder);
+ } else if (ACTION_SYNCHRONIZE_FOLDER.equals(intent.getAction())) {
+ processOperations(folder, isession, istore, ifolder);
+ synchronizeMessages(account, folder, ifolder, state);
+ }
- } catch (Throwable ex) {
- Log.e(
- Helper.TAG,
- folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
- reportError(account.name, folder.name, ex);
+ } catch (Throwable ex) {
+ Log.e(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ reportError(account.name, folder.name, ex);
- db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
- } finally {
- if (shouldClose) {
- if (ifolder != null && ifolder.isOpen()) {
- db.folder().setFolderState(folder.id, "closing");
- try {
- ifolder.close(false);
- } catch (MessagingException ex) {
- Log.w(
- Helper.TAG,
- folder.name
- + " "
- + ex
- + "\n"
- + Log.getStackTraceString(ex));
- }
- }
- db.folder().setFolderState(folder.id, null);
- }
- }
- } finally {
- wl.release();
+ db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
+ } finally {
+ if (shouldClose) {
+ if (ifolder != null && ifolder.isOpen()) {
+ db.folder().setFolderState(folder.id, "closing");
+ try {
+ ifolder.close(false);
+ } catch (MessagingException ex) {
+ Log.w(Helper.TAG,
+ folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
}
}
- });
+ db.folder().setFolderState(folder.id, null);
+ }
+ }
+ } finally {
+ wl.release();
+ }
}
- };
+ });
+ }
+ };
// Listen for folder operations
IntentFilter f = new IntentFilter();
@@ -1333,17 +1162,16 @@ public class ServiceSynchronize extends LifecycleService {
}
// Keep alive alarm receiver
- BroadcastReceiver alarm =
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Receiver runs on main thread
- // Receiver has a wake lock for ~10 seconds
- EntityLog.log(context, account.name + " keep alive wake lock=" + wl0.isHeld());
- state.thread.interrupt();
- yieldWakelock();
- }
- };
+ BroadcastReceiver alarm = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Receiver runs on main thread
+ // Receiver has a wake lock for ~10 seconds
+ EntityLog.log(context, account.name + " keep alive wake lock=" + wl0.isHeld());
+ state.thread.interrupt();
+ yieldWakelock();
+ }
+ };
String id = BuildConfig.APPLICATION_ID + ".POLL." + account.id;
PendingIntent pi =
@@ -1356,10 +1184,8 @@ public class ServiceSynchronize extends LifecycleService {
while (state.running) {
// Schedule keep alive alarm
EntityLog.log(this, account.name + " wait=" + account.poll_interval);
- am.setAndAllowWhileIdle(
- AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + account.poll_interval * 60 * 1000L,
- pi);
+ am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + account.poll_interval * 60 * 1000L, pi);
try {
wl0.release();
@@ -1414,22 +1240,18 @@ public class ServiceSynchronize extends LifecycleService {
// Close store
try {
- Thread t =
- new Thread(
- new Runnable() {
- @Override
- public void run() {
- try {
- EntityLog.log(ServiceSynchronize.this, account.name + " store closing");
- istore.close();
- EntityLog.log(ServiceSynchronize.this, account.name + " store closed");
- } catch (Throwable ex) {
- Log.w(
- Helper.TAG,
- account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
- }
- }
- });
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ EntityLog.log(ServiceSynchronize.this, account.name + " store closing");
+ istore.close();
+ EntityLog.log(ServiceSynchronize.this, account.name + " store closed");
+ } catch (Throwable ex) {
+ Log.w(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ }
+ }
+ });
t.start();
try {
t.join(MessageHelper.NETWORK_TIMEOUT);
@@ -1474,9 +1296,8 @@ public class ServiceSynchronize extends LifecycleService {
}
}
- private void processOperations(
- EntityFolder folder, Session isession, IMAPStore istore, IMAPFolder ifolder)
- throws MessagingException, JSONException, IOException {
+ private void processOperations(EntityFolder folder, Session isession, IMAPStore istore,
+ IMAPFolder ifolder) throws MessagingException, JSONException, IOException {
synchronized (lock) {
try {
Log.i(Helper.TAG, folder.name + " start process");
@@ -1486,17 +1307,8 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(Helper.TAG, folder.name + " pending operations=" + ops.size());
for (EntityOperation op : ops) {
try {
- Log.i(
- Helper.TAG,
- folder.name
- + " start op="
- + op.id
- + "/"
- + op.name
- + " msg="
- + op.message
- + " args="
- + op.args);
+ Log.i(Helper.TAG, folder.name + " start op=" + op.id + "/" + op.name + " msg="
+ + op.message + " args=" + op.args);
EntityMessage message = db.message().getMessage(op.message);
try {
@@ -1506,11 +1318,9 @@ public class ServiceSynchronize extends LifecycleService {
db.message().setMessageError(message.id, null);
- if (message.uid == null
- && (EntityOperation.SEEN.equals(op.name)
- || EntityOperation.DELETE.equals(op.name)
- || EntityOperation.MOVE.equals(op.name)
- || EntityOperation.HEADERS.equals(op.name))) {
+ if (message.uid == null && (EntityOperation.SEEN.equals(op.name)
+ || EntityOperation.DELETE.equals(op.name) || EntityOperation.MOVE.equals(op.name)
+ || EntityOperation.HEADERS.equals(op.name))) {
throw new IllegalArgumentException(op.name + " without uid " + op.args);
}
@@ -1550,8 +1360,7 @@ public class ServiceSynchronize extends LifecycleService {
db.message().setMessageError(message.id, Helper.formatThrowable(ex));
}
- if (ex instanceof MessageRemovedException
- || ex instanceof FolderNotFoundException
+ if (ex instanceof MessageRemovedException || ex instanceof FolderNotFoundException
|| ex instanceof SendFailedException) {
Log.w(Helper.TAG, "Unrecoverable " + ex + "\n" + Log.getStackTraceString(ex));
@@ -1579,9 +1388,8 @@ public class ServiceSynchronize extends LifecycleService {
}
}
- private void doSeen(
- EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db)
- throws MessagingException, JSONException {
+ private void doSeen(EntityFolder folder, IMAPFolder ifolder, EntityMessage message,
+ JSONArray jargs, DB db) throws MessagingException, JSONException {
// Mark message (un)seen
boolean seen = jargs.getBoolean(0);
if (message.seen == seen) {
@@ -1598,9 +1406,8 @@ public class ServiceSynchronize extends LifecycleService {
db.message().setMessageSeen(message.id, seen);
}
- private void doFlag(
- EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db)
- throws MessagingException, JSONException {
+ private void doFlag(EntityFolder folder, IMAPFolder ifolder, EntityMessage message,
+ JSONArray jargs, DB db) throws MessagingException, JSONException {
// Star/unstar message
boolean flagged = jargs.getBoolean(0);
Message imessage = ifolder.getMessageByUID(message.uid);
@@ -1613,13 +1420,8 @@ public class ServiceSynchronize extends LifecycleService {
db.message().setMessageFlagged(message.id, flagged);
}
- private void doAdd(
- EntityFolder folder,
- Session isession,
- IMAPFolder ifolder,
- EntityMessage message,
- JSONArray jargs,
- DB db)
+ private void doAdd(EntityFolder folder, Session isession, IMAPFolder ifolder,
+ EntityMessage message, JSONArray jargs, DB db)
throws MessagingException, JSONException, IOException {
// Append message
List attachments = db.attachment().getAttachments(message.id);
@@ -1638,14 +1440,8 @@ public class ServiceSynchronize extends LifecycleService {
}
}
- private void doMove(
- EntityFolder folder,
- Session isession,
- IMAPStore istore,
- IMAPFolder ifolder,
- EntityMessage message,
- JSONArray jargs,
- DB db)
+ private void doMove(EntityFolder folder, Session isession, IMAPStore istore, IMAPFolder ifolder,
+ EntityMessage message, JSONArray jargs, DB db)
throws JSONException, MessagingException, IOException {
// Move message
long id = jargs.getLong(0);
@@ -1679,9 +1475,8 @@ public class ServiceSynchronize extends LifecycleService {
}
}
- private void doDelete(
- EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db)
- throws MessagingException, JSONException {
+ private void doDelete(EntityFolder folder, IMAPFolder ifolder, EntityMessage message,
+ JSONArray jargs, DB db) throws MessagingException, JSONException {
// Delete message
Message imessage = ifolder.getMessageByUID(message.uid);
if (imessage == null) {
@@ -1741,8 +1536,7 @@ public class ServiceSynchronize extends LifecycleService {
// Send message
Address[] to = imessage.getAllRecipients();
itransport.sendMessage(imessage, to);
- Log.i(
- Helper.TAG,
+ Log.i(Helper.TAG,
"Sent via " + ident.host + "/" + ident.user + " to " + TextUtils.join(", ", to));
try {
@@ -1818,13 +1612,8 @@ public class ServiceSynchronize extends LifecycleService {
db.message().setMessageContent(message.id, true);
}
- private void doAttachment(
- EntityFolder folder,
- EntityOperation op,
- IMAPFolder ifolder,
- EntityMessage message,
- JSONArray jargs,
- DB db)
+ private void doAttachment(EntityFolder folder, EntityOperation op, IMAPFolder ifolder,
+ EntityMessage message, JSONArray jargs, DB db)
throws JSONException, MessagingException, IOException {
// Download attachment
int sequence = jargs.getInt(0);
@@ -1905,9 +1694,8 @@ public class ServiceSynchronize extends LifecycleService {
}
}
- private void synchronizeMessages(
- EntityAccount account, EntityFolder folder, IMAPFolder ifolder, ServiceState state)
- throws MessagingException, IOException {
+ private void synchronizeMessages(EntityAccount account, EntityFolder folder, IMAPFolder ifolder,
+ ServiceState state) throws MessagingException, IOException {
DB db = DB.getInstance(this);
try {
Log.v(Helper.TAG, folder.name + " start sync after=" + folder.after);
@@ -1940,14 +1728,8 @@ public class ServiceSynchronize extends LifecycleService {
// Reduce list of local uids
long search = SystemClock.elapsedRealtime();
Message[] imessages = ifolder.search(new ReceivedDateTerm(ComparisonTerm.GE, new Date(ago)));
- Log.i(
- Helper.TAG,
- folder.name
- + " remote count="
- + imessages.length
- + " search="
- + (SystemClock.elapsedRealtime() - search)
- + " ms");
+ Log.i(Helper.TAG, folder.name + " remote count=" + imessages.length + " search="
+ + (SystemClock.elapsedRealtime() - search) + " ms");
FetchProfile fp = new FetchProfile();
fp.add(UIDFolder.FetchProfileItem.UID);
@@ -1955,8 +1737,7 @@ public class ServiceSynchronize extends LifecycleService {
ifolder.fetch(imessages, fp);
long fetch = SystemClock.elapsedRealtime();
- Log.i(
- Helper.TAG,
+ Log.i(Helper.TAG,
folder.name + " remote fetched=" + (SystemClock.elapsedRealtime() - fetch) + " ms");
for (Message imessage : imessages) {
@@ -2013,14 +1794,8 @@ public class ServiceSynchronize extends LifecycleService {
if (full.size() > 0) {
long headers = SystemClock.elapsedRealtime();
ifolder.fetch(full.toArray(new Message[0]), fp);
- Log.i(
- Helper.TAG,
- folder.name
- + " fetched headers="
- + full.size()
- + " "
- + (SystemClock.elapsedRealtime() - headers)
- + " ms");
+ Log.i(Helper.TAG, folder.name + " fetched headers=" + full.size() + " "
+ + (SystemClock.elapsedRealtime() - headers) + " ms");
}
for (int j = isub.length - 1; j >= 0; j--) {
@@ -2095,9 +1870,8 @@ public class ServiceSynchronize extends LifecycleService {
}
}
- static Long synchronizeMessage(
- Context context, EntityFolder folder, IMAPFolder ifolder, IMAPMessage imessage, boolean found)
- throws MessagingException, IOException {
+ static Long synchronizeMessage(Context context, EntityFolder folder, IMAPFolder ifolder,
+ IMAPMessage imessage, boolean found) throws MessagingException, IOException {
long uid = ifolder.getUID(imessage);
if (imessage.isExpunged()) {
@@ -2128,43 +1902,19 @@ public class ServiceSynchronize extends LifecycleService {
String reference =
(refs.length == 1 && refs[0].indexOf(BuildConfig.APPLICATION_ID) > 0 ? refs[0] : msgid);
Log.i(Helper.TAG, "Searching for " + msgid + " / " + reference);
- for (EntityMessage dup :
- db.message().getMessageByMsgId(folder.account, msgid, reference, found)) {
+ for (EntityMessage dup : db.message().getMessageByMsgId(folder.account, msgid, reference,
+ found)) {
EntityFolder dfolder = db.folder().getFolder(dup.folder);
boolean outbox = EntityFolder.OUTBOX.equals(dfolder.type);
- Log.i(
- Helper.TAG,
- folder.name
- + " found as id="
- + dup.id
- + "/"
- + dup.uid
- + " folder="
- + dfolder.type
- + ":"
- + dup.folder
- + "/"
- + folder.type
- + ":"
- + folder.id
- + " msgid="
- + dup.msgid
- + " thread="
- + dup.thread);
+ Log.i(Helper.TAG,
+ folder.name + " found as id=" + dup.id + "/" + dup.uid + " folder=" + dfolder.type + ":"
+ + dup.folder + "/" + folder.type + ":" + folder.id + " msgid=" + dup.msgid
+ + " thread=" + dup.thread);
if (dup.folder.equals(folder.id) || outbox) {
String thread = helper.getThreadId(uid);
- Log.i(
- Helper.TAG,
- folder.name
- + " found as id="
- + dup.id
- + "/"
- + uid
- + " msgid="
- + msgid
- + " thread="
- + thread);
+ Log.i(Helper.TAG, folder.name + " found as id=" + dup.id + "/" + uid + " msgid=" + msgid
+ + " thread=" + thread);
dup.folder = folder.id;
dup.uid = uid;
dup.msgid = msgid;
@@ -2211,8 +1961,8 @@ public class ServiceSynchronize extends LifecycleService {
message.ui_found = found;
message.ui_ignored = false;
- if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
- == PackageManager.PERMISSION_GRANTED) {
+ if (ContextCompat.checkSelfPermission(context,
+ Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
try {
if (message.from != null) {
for (int i = 0; i < message.from.length; i++) {
@@ -2220,16 +1970,11 @@ public class ServiceSynchronize extends LifecycleService {
Cursor cursor = null;
try {
ContentResolver resolver = context.getContentResolver();
- cursor =
- resolver.query(
- ContactsContract.CommonDataKinds.Email.CONTENT_URI,
- new String[] {
- ContactsContract.CommonDataKinds.Photo.CONTACT_ID,
- ContactsContract.Contacts.DISPLAY_NAME
- },
- ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?",
- new String[] {email},
- null);
+ cursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
+ new String[] {ContactsContract.CommonDataKinds.Photo.CONTACT_ID,
+ ContactsContract.Contacts.DISPLAY_NAME},
+ ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?", new String[] {email},
+ null);
if (cursor.moveToNext()) {
int colContactId =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID);
@@ -2264,17 +2009,8 @@ public class ServiceSynchronize extends LifecycleService {
int sequence = 1;
for (EntityAttachment attachment : helper.getAttachments()) {
- Log.i(
- Helper.TAG,
- folder.name
- + " attachment seq="
- + sequence
- + " name="
- + attachment.name
- + " type="
- + attachment.type
- + " cid="
- + attachment.cid);
+ Log.i(Helper.TAG, folder.name + " attachment seq=" + sequence + " name=" + attachment.name
+ + " type=" + attachment.type + " cid=" + attachment.cid);
if (!TextUtils.isEmpty(attachment.cid)
&& db.attachment().getAttachment(message.id, attachment.cid) != null) {
Log.i(Helper.TAG, "Skipping duplicated CID");
@@ -2289,8 +2025,7 @@ public class ServiceSynchronize extends LifecycleService {
message.seen = seen;
message.ui_seen = seen;
db.message().updateMessage(message);
- Log.i(
- Helper.TAG,
+ Log.i(Helper.TAG,
folder.name + " updated id=" + message.id + " uid=" + message.uid + " seen=" + seen);
}
@@ -2298,15 +2033,8 @@ public class ServiceSynchronize extends LifecycleService {
message.flagged = flagged;
message.ui_flagged = flagged;
db.message().updateMessage(message);
- Log.i(
- Helper.TAG,
- folder.name
- + " updated id="
- + message.id
- + " uid="
- + message.uid
- + " flagged="
- + flagged);
+ Log.i(Helper.TAG, folder.name + " updated id=" + message.id + " uid=" + message.uid
+ + " flagged=" + flagged);
}
if (message.ui_hide) {
@@ -2319,9 +2047,8 @@ public class ServiceSynchronize extends LifecycleService {
return message.id;
}
- private static void downloadMessage(
- Context context, EntityFolder folder, IMAPFolder ifolder, IMAPMessage imessage, long id)
- throws MessagingException, IOException {
+ private static void downloadMessage(Context context, EntityFolder folder, IMAPFolder ifolder,
+ IMAPMessage imessage, long id) throws MessagingException, IOException {
DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id);
if (message == null) {
@@ -2370,8 +2097,7 @@ public class ServiceSynchronize extends LifecycleService {
if (!metered || (message.size != null && message.size < MESSAGE_AUTO_DOWNLOAD_SIZE)) {
message.write(context, helper.getHtml());
db.message().setMessageContent(message.id, true);
- Log.i(
- Helper.TAG,
+ Log.i(Helper.TAG,
folder.name + " downloaded message id=" + message.id + " size=" + message.size);
}
}
@@ -2387,15 +2113,8 @@ public class ServiceSynchronize extends LifecycleService {
}
attachment.part = iattachments.get(i).part;
attachment.download(context, db);
- Log.i(
- Helper.TAG,
- folder.name
- + " downloaded message id="
- + message.id
- + " attachment="
- + attachment.name
- + " size="
- + message.size);
+ Log.i(Helper.TAG, folder.name + " downloaded message id=" + message.id + " attachment="
+ + attachment.name + " size=" + message.size);
}
}
}
@@ -2415,20 +2134,18 @@ public class ServiceSynchronize extends LifecycleService {
public void onAvailable(Network network) {
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
NetworkInfo ni = cm.getNetworkInfo(network);
- EntityLog.log(
- ServiceSynchronize.this,
+ EntityLog.log(ServiceSynchronize.this,
"Network available " + network + " running=" + running + " " + ni);
if (!running) {
running = true;
- lifecycle.submit(
- new Runnable() {
- @Override
- public void run() {
- Log.i(Helper.TAG, "Starting service");
- start();
- }
- });
+ lifecycle.submit(new Runnable() {
+ @Override
+ public void run() {
+ Log.i(Helper.TAG, "Starting service");
+ start();
+ }
+ });
}
}
@@ -2439,19 +2156,18 @@ public class ServiceSynchronize extends LifecycleService {
if (running) {
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
NetworkInfo ani = (network == null ? null : cm.getActiveNetworkInfo());
- EntityLog.log(
- ServiceSynchronize.this, "Network active=" + (ani == null ? null : ani.toString()));
+ EntityLog.log(ServiceSynchronize.this,
+ "Network active=" + (ani == null ? null : ani.toString()));
if (ani == null || !ani.isConnected()) {
EntityLog.log(ServiceSynchronize.this, "Network disconnected=" + ani);
running = false;
lastLost = new Date().getTime();
- lifecycle.submit(
- new Runnable() {
- @Override
- public void run() {
- stop();
- }
- });
+ lifecycle.submit(new Runnable() {
+ @Override
+ public void run() {
+ stop();
+ }
+ });
}
}
}
@@ -2460,123 +2176,113 @@ public class ServiceSynchronize extends LifecycleService {
EntityLog.log(ServiceSynchronize.this, "Main start");
state = new ServiceState();
- state.thread =
- new Thread(
- new Runnable() {
- private List threadState = new ArrayList<>();
-
- @Override
- public void run() {
- PowerManager pm = getSystemService(PowerManager.class);
- PowerManager.WakeLock wl =
- pm.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":start");
- try {
- wl.acquire();
-
- DB db = DB.getInstance(ServiceSynchronize.this);
-
- outbox = db.folder().getOutbox();
- if (outbox == null) {
- EntityLog.log(ServiceSynchronize.this, "No outbox, halt");
- stopSelf();
- return;
- }
+ state.thread = new Thread(new Runnable() {
+ private List threadState = new ArrayList<>();
+
+ @Override
+ public void run() {
+ PowerManager pm = getSystemService(PowerManager.class);
+ PowerManager.WakeLock wl =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":start");
+ try {
+ wl.acquire();
- List accounts = db.account().getAccounts(true);
- if (accounts.size() == 0) {
- EntityLog.log(ServiceSynchronize.this, "No accounts, halt");
- stopSelf();
- return;
- }
+ DB db = DB.getInstance(ServiceSynchronize.this);
- long ago = new Date().getTime() - lastLost;
- if (ago < RECONNECT_BACKOFF) {
- try {
- long backoff = RECONNECT_BACKOFF - ago;
- EntityLog.log(ServiceSynchronize.this, "Main backoff=" + (backoff / 1000));
- Thread.sleep(backoff);
- } catch (InterruptedException ex) {
- Log.w(Helper.TAG, "main backoff " + ex.toString());
- return;
- }
- }
+ outbox = db.folder().getOutbox();
+ if (outbox == null) {
+ EntityLog.log(ServiceSynchronize.this, "No outbox, halt");
+ stopSelf();
+ return;
+ }
- // Start monitoring outbox
- IntentFilter f = new IntentFilter();
- f.addAction(ACTION_SYNCHRONIZE_FOLDER);
- f.addAction(ACTION_PROCESS_OPERATIONS);
- f.addDataType("account/outbox");
- LocalBroadcastManager lbm =
- LocalBroadcastManager.getInstance(ServiceSynchronize.this);
- lbm.registerReceiver(outboxReceiver, f);
+ List accounts = db.account().getAccounts(true);
+ if (accounts.size() == 0) {
+ EntityLog.log(ServiceSynchronize.this, "No accounts, halt");
+ stopSelf();
+ return;
+ }
- db.folder().setFolderState(outbox.id, "connected");
- db.folder().setFolderError(outbox.id, null);
+ long ago = new Date().getTime() - lastLost;
+ if (ago < RECONNECT_BACKOFF) {
+ try {
+ long backoff = RECONNECT_BACKOFF - ago;
+ EntityLog.log(ServiceSynchronize.this, "Main backoff=" + (backoff / 1000));
+ Thread.sleep(backoff);
+ } catch (InterruptedException ex) {
+ Log.w(Helper.TAG, "main backoff " + ex.toString());
+ return;
+ }
+ }
- lbm.sendBroadcast(
- new Intent(ACTION_PROCESS_OPERATIONS)
- .setType("account/outbox")
- .putExtra("folder", outbox.id));
-
- // Start monitoring accounts
- for (final EntityAccount account : accounts) {
- Log.i(Helper.TAG, account.host + "/" + account.user + " run");
- final ServiceState astate = new ServiceState();
- astate.thread =
- new Thread(
- new Runnable() {
- @Override
- public void run() {
- try {
- monitorAccount(account, astate);
- } catch (Throwable ex) {
- // Fall-safe
- Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
- }
- }
- },
- "sync.account." + account.id);
- astate.thread.start();
- threadState.add(astate);
- }
+ // Start monitoring outbox
+ IntentFilter f = new IntentFilter();
+ f.addAction(ACTION_SYNCHRONIZE_FOLDER);
+ f.addAction(ACTION_PROCESS_OPERATIONS);
+ f.addDataType("account/outbox");
+ LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(ServiceSynchronize.this);
+ lbm.registerReceiver(outboxReceiver, f);
+
+ db.folder().setFolderState(outbox.id, "connected");
+ db.folder().setFolderError(outbox.id, null);
+
+ lbm.sendBroadcast(new Intent(ACTION_PROCESS_OPERATIONS).setType("account/outbox")
+ .putExtra("folder", outbox.id));
+
+ // Start monitoring accounts
+ for (final EntityAccount account : accounts) {
+ Log.i(Helper.TAG, account.host + "/" + account.user + " run");
+ final ServiceState astate = new ServiceState();
+ astate.thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ monitorAccount(account, astate);
+ } catch (Throwable ex) {
+ // Fall-safe
+ Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
+ }
+ }
+ }, "sync.account." + account.id);
+ astate.thread.start();
+ threadState.add(astate);
+ }
- EntityLog.log(ServiceSynchronize.this, "Main started");
+ EntityLog.log(ServiceSynchronize.this, "Main started");
- try {
- yieldWakelock();
- wl.release();
- state.semaphore.acquire();
- } catch (InterruptedException ex) {
- Log.w(Helper.TAG, "main wait " + ex.toString());
- } finally {
- wl.acquire();
- }
+ try {
+ yieldWakelock();
+ wl.release();
+ state.semaphore.acquire();
+ } catch (InterruptedException ex) {
+ Log.w(Helper.TAG, "main wait " + ex.toString());
+ } finally {
+ wl.acquire();
+ }
- // Stop monitoring accounts
- for (ServiceState astate : threadState) {
- astate.running = false;
- astate.semaphore.release();
- join(astate.thread);
- }
- threadState.clear();
+ // Stop monitoring accounts
+ for (ServiceState astate : threadState) {
+ astate.running = false;
+ astate.semaphore.release();
+ join(astate.thread);
+ }
+ threadState.clear();
- // Stop monitoring outbox
- lbm.unregisterReceiver(outboxReceiver);
- Log.i(Helper.TAG, outbox.name + " unlisten operations");
- db.folder().setFolderState(outbox.id, null);
+ // Stop monitoring outbox
+ lbm.unregisterReceiver(outboxReceiver);
+ Log.i(Helper.TAG, outbox.name + " unlisten operations");
+ db.folder().setFolderState(outbox.id, null);
- EntityLog.log(ServiceSynchronize.this, "Main exited");
- } catch (Throwable ex) {
- // Fail-safe
- Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
- } finally {
- wl.release();
- EntityLog.log(ServiceSynchronize.this, "Start wake lock=" + wl.isHeld());
- }
- }
- },
- "sync.main");
+ EntityLog.log(ServiceSynchronize.this, "Main exited");
+ } catch (Throwable ex) {
+ // Fail-safe
+ Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
+ } finally {
+ wl.release();
+ EntityLog.log(ServiceSynchronize.this, "Start wake lock=" + wl.isHeld());
+ }
+ }
+ }, "sync.main");
state.thread.setPriority(THREAD_PRIORITY_BACKGROUND); // will be inherited
state.thread.start();
yieldWakelock();
@@ -2605,85 +2311,77 @@ public class ServiceSynchronize extends LifecycleService {
private void queue_reload() {
if (running) {
- lifecycle.submit(
- new Runnable() {
- @Override
- public void run() {
- stop();
- start();
- }
- });
+ lifecycle.submit(new Runnable() {
+ @Override
+ public void run() {
+ stop();
+ start();
+ }
+ });
}
}
private void queue_start() {
if (!running) {
running = true;
- lifecycle.submit(
- new Runnable() {
- @Override
- public void run() {
- start();
- }
- });
+ lifecycle.submit(new Runnable() {
+ @Override
+ public void run() {
+ start();
+ }
+ });
}
}
private void queue_stop() {
if (running) {
running = false;
- lifecycle.submit(
- new Runnable() {
- @Override
- public void run() {
- stop();
- stopSelf();
- }
- });
+ lifecycle.submit(new Runnable() {
+ @Override
+ public void run() {
+ stop();
+ stopSelf();
+ }
+ });
}
}
- private BroadcastReceiver outboxReceiver =
- new BroadcastReceiver() {
+ private BroadcastReceiver outboxReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ Log.v(Helper.TAG, outbox.name + " run operations");
+
+ executor.submit(new Runnable() {
@Override
- public void onReceive(final Context context, Intent intent) {
- Log.v(Helper.TAG, outbox.name + " run operations");
-
- executor.submit(
- new Runnable() {
- @Override
- public void run() {
- PowerManager pm = getSystemService(PowerManager.class);
- PowerManager.WakeLock wl =
- pm.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":outbox");
+ public void run() {
+ PowerManager pm = getSystemService(PowerManager.class);
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ BuildConfig.APPLICATION_ID + ":outbox");
- try {
- wl.acquire();
- DB db = DB.getInstance(context);
- try {
- Log.i(Helper.TAG, outbox.name + " start operations");
- db.folder().setFolderState(outbox.id, "syncing");
- processOperations(outbox, null, null, null);
- db.folder().setFolderError(outbox.id, null);
- } catch (Throwable ex) {
- Log.e(
- Helper.TAG,
- outbox.name + " " + ex + "\n" + Log.getStackTraceString(ex));
- reportError(null, outbox.name, ex);
-
- db.folder().setFolderError(outbox.id, Helper.formatThrowable(ex));
- } finally {
- Log.i(Helper.TAG, outbox.name + " end operations");
- db.folder().setFolderState(outbox.id, null);
- }
- } finally {
- wl.release();
- EntityLog.log(ServiceSynchronize.this, "Outbox wake lock=" + wl.isHeld());
- }
- }
- });
+ try {
+ wl.acquire();
+ DB db = DB.getInstance(context);
+ try {
+ Log.i(Helper.TAG, outbox.name + " start operations");
+ db.folder().setFolderState(outbox.id, "syncing");
+ processOperations(outbox, null, null, null);
+ db.folder().setFolderError(outbox.id, null);
+ } catch (Throwable ex) {
+ Log.e(Helper.TAG, outbox.name + " " + ex + "\n" + Log.getStackTraceString(ex));
+ reportError(null, outbox.name, ex);
+
+ db.folder().setFolderError(outbox.id, Helper.formatThrowable(ex));
+ } finally {
+ Log.i(Helper.TAG, outbox.name + " end operations");
+ db.folder().setFolderState(outbox.id, null);
+ }
+ } finally {
+ wl.release();
+ EntityLog.log(ServiceSynchronize.this, "Outbox wake lock=" + wl.isHeld());
+ }
}
+ });
+ }
};
}
@@ -2717,21 +2415,21 @@ public class ServiceSynchronize extends LifecycleService {
}
public static void start(Context context) {
- ContextCompat.startForegroundService(
- context, new Intent(context, ServiceSynchronize.class).setAction("start"));
+ ContextCompat.startForegroundService(context,
+ new Intent(context, ServiceSynchronize.class).setAction("start"));
}
public static void stop(Context context) {
- ContextCompat.startForegroundService(
- context, new Intent(context, ServiceSynchronize.class).setAction("stop"));
+ ContextCompat.startForegroundService(context,
+ new Intent(context, ServiceSynchronize.class).setAction("stop"));
}
public static void reload(Context context, String reason) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("enabled", true)) {
Log.i(Helper.TAG, "Reload because of '" + reason + "'");
- ContextCompat.startForegroundService(
- context, new Intent(context, ServiceSynchronize.class).setAction("reload"));
+ ContextCompat.startForegroundService(context,
+ new Intent(context, ServiceSynchronize.class).setAction("reload"));
}
}