Display navigation folders on accounts start screen

pull/214/head
M66B 5 months ago
parent 7916ff1bac
commit 90baa403e7

@ -1006,7 +1006,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
DB db = DB.getInstance(this);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
db.account().liveAccountFolder().observe(owner, new Observer<List<TupleAccountFolder>>() {
db.account().liveAccountFolder(false).observe(owner, new Observer<List<TupleAccountFolder>>() {
@Override
public void onChanged(@Nullable List<TupleAccountFolder> accounts) {
if (accounts == null)

@ -91,7 +91,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
private int textColorTertiary;
private boolean debug;
private List<TupleAccountEx> items = new ArrayList<>();
private List<TupleAccountFolder> items = new ArrayList<>();
private NumberFormat NF = NumberFormat.getNumberInstance();
private DateFormat DTF;
@ -196,133 +196,191 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
btnHelp.setOnClickListener(null);
}
private void bindTo(TupleAccountEx account) {
view.setActivated(account.tbd != null);
view.setAlpha(account.synchronize ? 1.0f : Helper.LOW_LIGHT);
vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color);
vwColor.setVisibility(ActivityBilling.isPro(context) ? View.VISIBLE : View.INVISIBLE);
ivSync.setImageResource(account.synchronize ? R.drawable.twotone_sync_24 : R.drawable.twotone_sync_disabled_24);
ivSync.setContentDescription(context.getString(account.synchronize ? R.string.title_legend_synchronize_on : R.string.title_legend_synchronize_off));
private void bindTo(TupleAccountFolder account) {
if (account.folderName == null) {
view.setActivated(account.tbd != null);
view.setAlpha(account.synchronize ? 1.0f : Helper.LOW_LIGHT);
vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color);
vwColor.setVisibility(ActivityBilling.isPro(context) ? View.VISIBLE : View.INVISIBLE);
ivSync.setImageResource(account.synchronize ? R.drawable.twotone_sync_24 : R.drawable.twotone_sync_disabled_24);
ivSync.setContentDescription(context.getString(account.synchronize ? R.string.title_legend_synchronize_on : R.string.title_legend_synchronize_off));
ivSync.setVisibility(View.VISIBLE);
ivOAuth.setImageDrawable(ContextCompat.getDrawable(context, account.auth_type == AUTH_TYPE_GMAIL
? R.drawable.twotone_android_24 : R.drawable.twotone_security_24));
ivOAuth.setVisibility(
settings && account.auth_type != AUTH_TYPE_PASSWORD ? View.VISIBLE : View.GONE);
ivPrimary.setVisibility(account.primary ? View.VISIBLE : View.GONE);
ivNotify.setVisibility(account.notify ? View.VISIBLE : View.GONE);
if (settings) {
tvName.setText(account.name);
tvName.setTextColor(account.protocol == EntityAccount.TYPE_IMAP
? textColorSecondary : colorWarning);
} else {
if (account.unseen > 0)
tvName.setText(context.getString(R.string.title_name_count, account.name, NF.format(account.unseen)));
else
tvName.setText(account.name);
ivOAuth.setImageDrawable(ContextCompat.getDrawable(context, account.auth_type == AUTH_TYPE_GMAIL
? R.drawable.twotone_android_24 : R.drawable.twotone_security_24));
ivOAuth.setVisibility(
settings && account.auth_type != AUTH_TYPE_PASSWORD ? View.VISIBLE : View.GONE);
ivPrimary.setVisibility(account.primary ? View.VISIBLE : View.GONE);
ivNotify.setVisibility(account.notify ? View.VISIBLE : View.GONE);
tvName.setTypeface(account.unseen > 0 ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvName.setTextColor(account.unseen > 0 ? colorUnread : textColorSecondary);
}
if (settings) {
tvName.setText(account.name);
tvName.setTextColor(account.protocol == EntityAccount.TYPE_IMAP
? textColorSecondary : colorWarning);
StringBuilder user = new StringBuilder(account.user);
if (account.provider != null && (BuildConfig.DEBUG || debug))
user.append(" (").append(account.provider).append(')');
tvUser.setText(user);
tvUser.setVisibility(View.VISIBLE);
if ("connected".equals(account.state)) {
ivState.setImageResource(R.drawable.twotone_cloud_done_24);
ivState.setContentDescription(context.getString(R.string.title_legend_connected));
} else if ("connecting".equals(account.state)) {
ivState.setImageResource(R.drawable.twotone_cloud_queue_24);
ivState.setContentDescription(context.getString(R.string.title_legend_connecting));
} else if ("closing".equals(account.state)) {
ivState.setImageResource(R.drawable.twotone_cancel_24);
ivState.setContentDescription(context.getString(R.string.title_legend_closing));
} else {
if (account.backoff_until == null) {
ivState.setImageResource(R.drawable.twotone_cloud_off_24);
ivState.setContentDescription(context.getString(R.string.title_legend_disconnected));
} else {
ivState.setImageResource(R.drawable.twotone_update_24);
ivState.setContentDescription(context.getString(R.string.title_legend_backoff));
}
}
ivState.setVisibility(account.synchronize || account.state != null ? View.VISIBLE : View.INVISIBLE);
tvHost.setText(String.format("%s:%d/%s",
account.host,
account.port,
EmailService.getEncryptionName(account.encryption)));
tvHost.setTextColor(account.insecure ? colorWarning : textColorTertiary);
tvHost.setVisibility(View.VISIBLE);
tvCreated.setVisibility(debug ? View.VISIBLE : View.GONE);
tvCreated.setText(context.getString(R.string.title_created_at,
account.created == null ? null : DTF.format(account.created)));
tvLast.setVisibility(compact ? View.GONE : View.VISIBLE);
tvLast.setText(context.getString(R.string.title_last_connected,
(account.last_connected == null ? "-" : DTF.format(account.last_connected)) +
(BuildConfig.DEBUG ?
"/" + (account.last_modified == null ? "-" : DTF.format(account.last_modified)) +
" " + account.poll_interval +
"/" + account.keep_alive_ok +
"/" + account.keep_alive_failed +
"/" + account.keep_alive_succeeded : "")));
tvBackoff.setText(context.getString(R.string.title_backoff_until,
account.backoff_until == null ? "-" : DTF.format(account.backoff_until)));
tvBackoff.setVisibility(account.backoff_until == null || !settings ? View.GONE : View.VISIBLE);
Integer percent = account.getQuotaPercentage();
boolean warning = (percent != null && percent > EntityAccount.QUOTA_WARNING);
tvUsage.setText(settings || percent == null ? null : NF.format(percent) + "%");
tvUsage.setVisibility(settings || percent == null || (compact && !warning) ? View.GONE : View.VISIBLE);
tvQuota.setText(context.getString(R.string.title_storage_quota,
(account.quota_usage == null ? "-" : Helper.humanReadableByteCount(account.quota_usage)),
(account.quota_limit == null ? "-" : Helper.humanReadableByteCount(account.quota_limit))));
tvQuota.setVisibility(settings && (account.quota_usage != null || account.quota_limit != null) ? View.VISIBLE : View.GONE);
tvUsage.setTextColor(warning ? colorWarning : textColorSecondary);
tvUsage.setTypeface(warning ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvQuota.setTextColor(warning ? colorWarning : textColorSecondary);
tvQuota.setTypeface(warning ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvMaxSize.setText(account.max_size == null ? null : Helper.humanReadableByteCount(account.max_size));
tvMaxSize.setVisibility(settings && account.max_size != null && BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
if (tvMaxSize.getVisibility() == View.VISIBLE)
tvQuota.setVisibility(View.VISIBLE);
tvId.setText(account.id + "/" + account.uuid);
tvId.setVisibility(settings && BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
tvCapabilities.setText(account.capabilities);
tvCapabilities.setVisibility(settings && (debug || BuildConfig.DEBUG) &&
!TextUtils.isEmpty(account.capabilities) ? View.VISIBLE : View.GONE);
tvIdentity.setVisibility(account.identities > 0 || !settings ? View.GONE : View.VISIBLE);
tvDrafts.setVisibility(account.drafts != null || !settings ? View.GONE : View.VISIBLE);
tvSent.setVisibility(account.protocol != EntityAccount.TYPE_IMAP ||
account.sent != null || !settings ? View.GONE : View.VISIBLE);
tvWarning.setText(account.warning);
tvWarning.setVisibility(account.warning == null || !settings ? View.GONE : View.VISIBLE);
tvError.setText(account.error);
tvError.setVisibility(account.error == null ? View.GONE : View.VISIBLE);
btnHelp.setVisibility(account.error == null ? View.GONE : View.VISIBLE);
ibInbox.setVisibility(settings ? View.GONE : View.VISIBLE);
grpSettings.setVisibility(settings ? View.VISIBLE : View.GONE);
} else {
view.setActivated(account.tbd != null);
view.setAlpha(account.synchronize ? 1.0f : Helper.LOW_LIGHT);
vwColor.setBackgroundColor(account.folderColor == null ? Color.TRANSPARENT : account.folderColor);
vwColor.setVisibility(ActivityBilling.isPro(context) ? View.VISIBLE : View.INVISIBLE);
ivSync.setVisibility(View.GONE);
ivOAuth.setVisibility(View.GONE);
ivPrimary.setVisibility(View.GONE);
ivNotify.setVisibility(View.GONE);
if (account.unseen > 0)
tvName.setText(context.getString(R.string.title_name_count, account.name, NF.format(account.unseen)));
tvName.setText(context.getString(R.string.title_name_count,
account.getName(context), NF.format(account.unseen)));
else
tvName.setText(account.name);
tvName.setText(account.getName(context));
tvName.setTypeface(account.unseen > 0 ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvName.setTextColor(account.unseen > 0 ? colorUnread : textColorSecondary);
}
StringBuilder user = new StringBuilder(account.user);
if (account.provider != null && (BuildConfig.DEBUG || debug))
user.append(" (").append(account.provider).append(')');
tvUser.setText(user);
if ("connected".equals(account.state)) {
ivState.setImageResource(R.drawable.twotone_cloud_done_24);
ivState.setContentDescription(context.getString(R.string.title_legend_connected));
} else if ("connecting".equals(account.state)) {
ivState.setImageResource(R.drawable.twotone_cloud_queue_24);
ivState.setContentDescription(context.getString(R.string.title_legend_connecting));
} else if ("closing".equals(account.state)) {
ivState.setImageResource(R.drawable.twotone_cancel_24);
ivState.setContentDescription(context.getString(R.string.title_legend_closing));
} else {
if (account.backoff_until == null) {
ivState.setImageResource(R.drawable.twotone_cloud_off_24);
ivState.setContentDescription(context.getString(R.string.title_legend_disconnected));
} else {
ivState.setImageResource(R.drawable.twotone_update_24);
ivState.setContentDescription(context.getString(R.string.title_legend_backoff));
}
tvUser.setVisibility(View.GONE);
ivState.setVisibility(View.GONE);
tvHost.setVisibility(View.GONE);
tvCreated.setVisibility(View.GONE);
tvLast.setVisibility(View.GONE);
tvBackoff.setVisibility(View.GONE);
tvUsage.setVisibility(View.GONE);
tvQuota.setVisibility(View.GONE);
tvMaxSize.setVisibility(View.GONE);
tvId.setVisibility(View.GONE);
tvCapabilities.setVisibility(View.GONE);
tvIdentity.setVisibility(View.GONE);
tvDrafts.setVisibility(View.GONE);
tvSent.setVisibility(View.GONE);
tvWarning.setVisibility(View.GONE);
tvError.setVisibility(View.GONE);
btnHelp.setVisibility(View.GONE);
ibInbox.setVisibility(View.GONE);
grpSettings.setVisibility(View.GONE);
}
ivState.setVisibility(account.synchronize || account.state != null ? View.VISIBLE : View.INVISIBLE);
tvHost.setText(String.format("%s:%d/%s",
account.host,
account.port,
EmailService.getEncryptionName(account.encryption)));
tvHost.setTextColor(account.insecure ? colorWarning : textColorTertiary);
tvCreated.setVisibility(debug ? View.VISIBLE : View.GONE);
tvCreated.setText(context.getString(R.string.title_created_at,
account.created == null ? null : DTF.format(account.created)));
tvLast.setVisibility(compact ? View.GONE : View.VISIBLE);
tvLast.setText(context.getString(R.string.title_last_connected,
(account.last_connected == null ? "-" : DTF.format(account.last_connected)) +
(BuildConfig.DEBUG ?
"/" + (account.last_modified == null ? "-" : DTF.format(account.last_modified)) +
" " + account.poll_interval +
"/" + account.keep_alive_ok +
"/" + account.keep_alive_failed +
"/" + account.keep_alive_succeeded : "")));
tvBackoff.setText(context.getString(R.string.title_backoff_until,
account.backoff_until == null ? "-" : DTF.format(account.backoff_until)));
tvBackoff.setVisibility(account.backoff_until == null || !settings ? View.GONE : View.VISIBLE);
Integer percent = account.getQuotaPercentage();
boolean warning = (percent != null && percent > EntityAccount.QUOTA_WARNING);
tvUsage.setText(settings || percent == null ? null : NF.format(percent) + "%");
tvUsage.setVisibility(settings || percent == null || (compact && !warning) ? View.GONE : View.VISIBLE);
tvQuota.setText(context.getString(R.string.title_storage_quota,
(account.quota_usage == null ? "-" : Helper.humanReadableByteCount(account.quota_usage)),
(account.quota_limit == null ? "-" : Helper.humanReadableByteCount(account.quota_limit))));
tvQuota.setVisibility(settings && (account.quota_usage != null || account.quota_limit != null) ? View.VISIBLE : View.GONE);
tvUsage.setTextColor(warning ? colorWarning : textColorSecondary);
tvUsage.setTypeface(warning ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvQuota.setTextColor(warning ? colorWarning : textColorSecondary);
tvQuota.setTypeface(warning ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvMaxSize.setText(account.max_size == null ? null : Helper.humanReadableByteCount(account.max_size));
tvMaxSize.setVisibility(settings && account.max_size != null && BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
if (tvMaxSize.getVisibility() == View.VISIBLE)
tvQuota.setVisibility(View.VISIBLE);
tvId.setText(account.id + "/" + account.uuid);
tvId.setVisibility(settings && BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
tvCapabilities.setText(account.capabilities);
tvCapabilities.setVisibility(settings && (debug || BuildConfig.DEBUG) &&
!TextUtils.isEmpty(account.capabilities) ? View.VISIBLE : View.GONE);
tvIdentity.setVisibility(account.identities > 0 || !settings ? View.GONE : View.VISIBLE);
tvDrafts.setVisibility(account.drafts != null || !settings ? View.GONE : View.VISIBLE);
tvSent.setVisibility(account.protocol != EntityAccount.TYPE_IMAP ||
account.sent != null || !settings ? View.GONE : View.VISIBLE);
tvWarning.setText(account.warning);
tvWarning.setVisibility(account.warning == null || !settings ? View.GONE : View.VISIBLE);
tvError.setText(account.error);
tvError.setVisibility(account.error == null ? View.GONE : View.VISIBLE);
btnHelp.setVisibility(account.error == null ? View.GONE : View.VISIBLE);
ibInbox.setVisibility(settings ? View.GONE : View.VISIBLE);
grpSettings.setVisibility(settings ? View.VISIBLE : View.GONE);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btnHelp) {
int pos = getAdapterPosition();
TupleAccountEx account = (pos == RecyclerView.NO_POSITION ? null : items.get(pos));
TupleAccountFolder account = (pos == RecyclerView.NO_POSITION ? null : items.get(pos));
if (account == null)
Helper.viewFAQ(context, 22);
else {
@ -345,7 +403,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
if (pos == RecyclerView.NO_POSITION)
return;
TupleAccountEx account = items.get(pos);
TupleAccountFolder account = items.get(pos);
if (account.tbd != null)
return;
@ -383,10 +441,17 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
}.execute(context, owner, args, "account:inbox");
} else {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(settings ? ActivitySetup.ACTION_EDIT_ACCOUNT : ActivityView.ACTION_VIEW_FOLDERS)
.putExtra("id", account.id)
.putExtra("protocol", account.protocol));
if (account.folderName == null)
lbm.sendBroadcast(
new Intent(settings ? ActivitySetup.ACTION_EDIT_ACCOUNT : ActivityView.ACTION_VIEW_FOLDERS)
.putExtra("id", account.id)
.putExtra("protocol", account.protocol));
else
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_MESSAGES)
.putExtra("account", account.id)
.putExtra("folder", account.folderId)
.putExtra("type", account.folderType));
}
}
@ -396,8 +461,8 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
if (pos == RecyclerView.NO_POSITION)
return false;
final TupleAccountEx account = items.get(pos);
if (account.tbd != null)
final TupleAccountFolder account = items.get(pos);
if (account.tbd != null || account.folderName != null)
return false;
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, view);
@ -811,7 +876,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
this.DTF = Helper.getDateTimeInstance(context, DateFormat.SHORT, DateFormat.MEDIUM);
setHasStableIds(true);
setHasStableIds(false);
owner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
@ -823,9 +888,12 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
});
}
public void set(@NonNull List<TupleAccountEx> accounts) {
public void set(@NonNull List<TupleAccountFolder> accounts) {
Log.i("Set accounts=" + accounts.size());
if (accounts.size() > 0)
TupleAccountFolder.sort(accounts, true, context);
DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, accounts), false);
items = accounts;
@ -865,10 +933,10 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
}
private static class DiffCallback extends DiffUtil.Callback {
private List<TupleAccountEx> prev = new ArrayList<>();
private List<TupleAccountEx> next = new ArrayList<>();
private List<TupleAccountFolder> prev = new ArrayList<>();
private List<TupleAccountFolder> next = new ArrayList<>();
DiffCallback(List<TupleAccountEx> prev, List<TupleAccountEx> next) {
DiffCallback(List<TupleAccountFolder> prev, List<TupleAccountFolder> next) {
this.prev.addAll(prev);
this.next.addAll(next);
}
@ -885,15 +953,15 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
TupleAccountEx f1 = prev.get(oldItemPosition);
TupleAccountEx f2 = next.get(newItemPosition);
TupleAccountFolder f1 = prev.get(oldItemPosition);
TupleAccountFolder f2 = next.get(newItemPosition);
return f1.id.equals(f2.id);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
TupleAccountEx f1 = prev.get(oldItemPosition);
TupleAccountEx f2 = next.get(newItemPosition);
TupleAccountFolder f1 = prev.get(oldItemPosition);
TupleAccountFolder f2 = next.get(newItemPosition);
return f1.equals(f2);
}
}
@ -903,7 +971,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
return items.get(position).id;
}
public TupleAccountEx getItemAtPosition(int pos) {
public TupleAccountFolder getItemAtPosition(int pos) {
if (pos >= 0 && pos < items.size())
return items.get(pos);
else
@ -923,7 +991,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
TupleAccountEx account = items.get(position);
TupleAccountFolder account = items.get(position);
holder.powner.recreate(account == null ? null : account.id);
holder.unwire();

@ -311,66 +311,8 @@ public class AdapterNavAccountFolder extends RecyclerView.Adapter<AdapterNavAcco
public void set(@NonNull List<TupleAccountFolder> accounts, boolean expanded, boolean folders) {
Log.i("Set nav accounts=" + accounts.size());
if (accounts.size() > 0) {
final Collator collator = Collator.getInstance(Locale.getDefault());
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
Collections.sort(accounts, new Comparator<TupleAccountFolder>() {
@Override
public int compare(TupleAccountFolder a1, TupleAccountFolder a2) {
// Account
if (nav_categories) {
int c = collator.compare(
a1.category == null ? "" : a1.category,
a2.category == null ? "" : a2.category);
if (c != 0)
return c;
}
int a = Integer.compare(
a1.order == null ? -1 : a1.order,
a2.order == null ? -1 : a2.order);
if (a != 0)
return a;
int p = -Boolean.compare(a1.primary, a2.primary);
if (p != 0)
return p;
int n = collator.compare(a1.name, a2.name);
if (n != 0)
return n;
// Folder
int o = Integer.compare(
a1.folderOrder == null ? -1 : a1.folderOrder,
a2.folderOrder == null ? -1 : a2.folderOrder);
if (o != 0)
return o;
int t1 = EntityFolder.FOLDER_SORT_ORDER.indexOf(a1.folderType);
int t2 = EntityFolder.FOLDER_SORT_ORDER.indexOf(a2.folderType);
int t = Integer.compare(t1, t2);
if (t != 0)
return t;
int s = -Boolean.compare(a1.folderSync, a2.folderSync);
if (s != 0)
return s;
if (a1.folderName == null && a2.folderName == null)
return 0;
else if (a1.folderName == null)
return -1;
else if (a2.folderName == null)
return 1;
return collator.compare(a1.getName(context), a2.getName(context));
}
});
}
if (accounts.size() > 0)
TupleAccountFolder.sort(accounts, nav_categories, context);
all = accounts;
if (!folders) {

@ -50,34 +50,11 @@ public interface DaoAccount {
LiveData<List<EntityAccount>> liveSynchronizingAccounts();
@Query("SELECT account.*" +
", (SELECT COUNT(DISTINCT" +
" CASE WHEN NOT message.hash IS NULL THEN message.hash" +
" WHEN NOT message.msgid IS NULL THEN message.msgid" +
" ELSE message.id END)" +
" FROM message" +
" JOIN folder ON folder.id = message.folder" +
" WHERE message.account = account.id" +
" AND folder.type <> '" + EntityFolder.OUTBOX + "'" +
" AND folder.count_unread" +
" AND NOT ui_seen" +
" AND NOT ui_hide) AS unseen" +
", (SELECT COUNT(identity.id)" +
" FROM identity" +
" WHERE identity.account = account.id" +
" AND identity.synchronize) AS identities" +
", drafts.id AS drafts, sent.id AS sent" +
" FROM account" +
" LEFT JOIN folder AS drafts ON drafts.account = account.id AND drafts.type = '" + EntityFolder.DRAFTS + "'" +
" LEFT JOIN folder AS sent ON sent.account = account.id AND sent.type = '" + EntityFolder.SENT + "'" +
" WHERE :all OR account.synchronize" +
" GROUP BY account.id" +
" ORDER BY account.category COLLATE NOCASE" +
", account.`order`" +
", account.`primary` DESC" +
", account.name COLLATE NOCASE")
LiveData<List<TupleAccountEx>> liveAccountsEx(boolean all);
@Query("SELECT account.*" +
", NULL AS folderId, NULL AS folderSeparator" +
", NULL AS folderType, -1 AS folderOrder" +
", NULL AS folderName, NULL AS folderDisplay, NULL AS folderColor" +
@ -96,11 +73,14 @@ public interface DaoAccount {
" AND NOT ui_seen" +
" AND NOT ui_hide) AS unseen" +
" FROM account" +
" WHERE account.synchronize" +
" LEFT JOIN folder AS drafts ON drafts.account = account.id AND drafts.type = '" + EntityFolder.DRAFTS + "'" +
" LEFT JOIN folder AS sent ON sent.account = account.id AND sent.type = '" + EntityFolder.SENT + "'" +
" WHERE (:settings OR account.synchronize)" +
" UNION " +
" SELECT account.*" +
", 0 AS identities, 0 AS drafts, 0 AS sent" +
", folder.id AS folderId, folder.separator AS folderSeparator" +
", folder.type AS folderType, folder.`order` AS folderOrder" +
", folder.name AS folderName, folder.display AS folderDisplay, folder.color AS folderColor" +
@ -120,10 +100,10 @@ public interface DaoAccount {
" FROM account" +
" JOIN folder ON folder.account = account.id" +
" LEFT JOIN operation ON operation.folder = folder.id AND operation.state = 'executing'" +
" WHERE account.synchronize" +
" AND folder.navigation" +
" WHERE (:settings OR account.synchronize)" +
" AND NOT :settings AND folder.navigation" +
" GROUP BY folder.id")
LiveData<List<TupleAccountFolder>> liveAccountFolder();
LiveData<List<TupleAccountFolder>> liveAccountFolder(boolean settings);
@Query("SELECT account.*" +
", SUM(folder.synchronize) AS folders" +

@ -58,9 +58,12 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
public class FragmentAccounts extends FragmentBase {
@ -183,8 +186,8 @@ public class FragmentAccounts extends FragmentBase {
if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
return null;
TupleAccountEx prev = adapter.getItemAtPosition(pos - 1);
TupleAccountEx account = adapter.getItemAtPosition(pos);
TupleAccountFolder prev = adapter.getItemAtPosition(pos - 1);
TupleAccountFolder account = adapter.getItemAtPosition(pos);
if (pos > 0 && prev == null)
return null;
if (account == null)
@ -309,15 +312,15 @@ public class FragmentAccounts extends FragmentBase {
final DB db = DB.getInstance(context);
// Observe accounts
db.account().liveAccountsEx(settings)
.observe(getViewLifecycleOwner(), new Observer<List<TupleAccountEx>>() {
db.account().liveAccountFolder(settings)
.observe(getViewLifecycleOwner(), new Observer<List<TupleAccountFolder>>() {
@Override
public void onChanged(@Nullable List<TupleAccountEx> accounts) {
public void onChanged(@Nullable List<TupleAccountFolder> accounts) {
if (accounts == null)
accounts = new ArrayList<>();
boolean authorized = true;
for (TupleAccountEx account : accounts)
for (TupleAccountFolder account : accounts)
if (account.auth_type != AUTH_TYPE_PASSWORD &&
!Helper.hasPermissions(getContext(), Helper.getOAuthPermissions())) {
authorized = false;

@ -1,42 +0,0 @@
package eu.faircode.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 <http://www.gnu.org/licenses/>.
Copyright 2018-2024 by Marcel Bokhorst (M66B)
*/
import java.util.Objects;
public class TupleAccountEx extends EntityAccount {
public int unseen;
public int identities; // synchronizing
public Long drafts;
public Long sent;
@Override
public boolean equals(Object obj) {
if (obj instanceof TupleAccountEx) {
TupleAccountEx other = (TupleAccountEx) obj;
return (super.equals(obj) &&
this.unseen == other.unseen &&
this.identities == other.identities &&
Objects.equals(this.drafts, other.drafts) &&
Objects.equals(this.sent, other.sent));
} else
return false;
}
}

@ -21,9 +21,18 @@ package eu.faircode.email;
import android.content.Context;
import java.text.Collator;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
public class TupleAccountFolder extends EntityAccount {
public int identities; // synchronizing
public Long drafts;
public Long sent;
public Long folderId;
public Character folderSeparator;
public String folderType;
@ -58,11 +67,76 @@ public class TupleAccountFolder extends EntityAccount {
return folderName.substring(s + 1);
}
static void sort(List<TupleAccountFolder> accounts, boolean nav_categories, Context context) {
final Collator collator = Collator.getInstance(Locale.getDefault());
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
Collections.sort(accounts, new Comparator<TupleAccountFolder>() {
@Override
public int compare(TupleAccountFolder a1, TupleAccountFolder a2) {
// Account
if (nav_categories) {
int c = collator.compare(
a1.category == null ? "" : a1.category,
a2.category == null ? "" : a2.category);
if (c != 0)
return c;
}
int a = Integer.compare(
a1.order == null ? -1 : a1.order,
a2.order == null ? -1 : a2.order);
if (a != 0)
return a;
int p = -Boolean.compare(a1.primary, a2.primary);
if (p != 0)
return p;
int n = collator.compare(a1.name, a2.name);
if (n != 0)
return n;
// Folder
int o = Integer.compare(
a1.folderOrder == null ? -1 : a1.folderOrder,
a2.folderOrder == null ? -1 : a2.folderOrder);
if (o != 0)
return o;
int t1 = EntityFolder.FOLDER_SORT_ORDER.indexOf(a1.folderType);
int t2 = EntityFolder.FOLDER_SORT_ORDER.indexOf(a2.folderType);
int t = Integer.compare(t1, t2);
if (t != 0)
return t;
int s = -Boolean.compare(a1.folderSync, a2.folderSync);
if (s != 0)
return s;
if (a1.folderName == null && a2.folderName == null)
return 0;
else if (a1.folderName == null)
return -1;
else if (a2.folderName == null)
return 1;
return collator.compare(a1.getName(context), a2.getName(context));
}
});
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TupleAccountFolder) {
TupleAccountFolder other = (TupleAccountFolder) obj;
return (super.equals(obj) &&
this.identities == other.identities &&
Objects.equals(this.drafts, other.drafts) &&
Objects.equals(this.sent, other.sent) &&
Objects.equals(this.folderId, other.folderId) &&
Objects.equals(this.folderSeparator, other.folderSeparator) &&
Objects.equals(this.folderType, other.folderType) &&

Loading…
Cancel
Save