Moved nav folders into nav accounts

pull/206/head
M66B 3 years ago
parent 66cf900bfe
commit 8b8b5288c0

@ -118,17 +118,14 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
private RecyclerView rvAccount;
private ImageButton ibExpanderUnified;
private RecyclerView rvUnified;
private ImageButton ibExpanderFolder;
private RecyclerView rvFolder;
private ImageButton ibExpanderMenu;
private RecyclerView rvMenu;
private ImageButton ibExpanderExtra;
private RecyclerView rvMenuExtra;
private Group grpOptions;
private AdapterNavAccount adapterNavAccount;
private AdapterNavAccountFolder adapterNavAccount;
private AdapterNavUnified adapterNavUnified;
private AdapterNavFolder adapterNavFolder;
private AdapterNavMenu adapterNavMenu;
private AdapterNavMenu adapterNavMenuExtra;
@ -297,8 +294,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
rvAccount = drawerContainer.findViewById(R.id.rvAccount);
ibExpanderUnified = drawerContainer.findViewById(R.id.ibExpanderUnified);
rvUnified = drawerContainer.findViewById(R.id.rvUnified);
ibExpanderFolder = drawerContainer.findViewById(R.id.ibExpanderFolder);
rvFolder = drawerContainer.findViewById(R.id.rvFolder);
ibExpanderMenu = drawerContainer.findViewById(R.id.ibExpanderMenu);
rvMenu = drawerContainer.findViewById(R.id.rvMenu);
ibExpanderExtra = drawerContainer.findViewById(R.id.ibExpanderExtra);
@ -418,18 +413,24 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
// Accounts
rvAccount.setLayoutManager(new LinearLayoutManager(this));
adapterNavAccount = new AdapterNavAccount(this, this);
adapterNavAccount = new AdapterNavAccountFolder(this, this);
rvAccount.setAdapter(adapterNavAccount);
boolean nav_account = prefs.getBoolean("nav_account", true);
ibExpanderAccount.setImageLevel(nav_account ? 0 /* less */ : 1 /* more */);
rvAccount.setVisibility(nav_account ? View.VISIBLE : View.GONE);
boolean nav_folder = prefs.getBoolean("nav_folder", true);
ibExpanderAccount.setImageLevel(nav_account || nav_folder ? 0 /* less */ : 1 /* more */);
rvAccount.setVisibility(nav_account || nav_folder ? View.VISIBLE : View.GONE);
ibExpanderAccount.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean nav_account = !prefs.getBoolean("nav_account", true);
prefs.edit().putBoolean("nav_account", nav_account).apply();
boolean nav_account = prefs.getBoolean("nav_account", true);
boolean nav_folder = prefs.getBoolean("nav_folder", true);
nav_account = !(nav_account || nav_folder);
prefs.edit()
.putBoolean("nav_account", nav_account)
.putBoolean("nav_folder", false)
.apply();
ibExpanderAccount.setImageLevel(nav_account ? 0 /* less */ : 1 /* more */);
rvAccount.setVisibility(nav_account ? View.VISIBLE : View.GONE);
}
@ -454,25 +455,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
}
});
// Navigation folders
rvFolder.setLayoutManager(new LinearLayoutManager(this));
adapterNavFolder = new AdapterNavFolder(this, this);
rvFolder.setAdapter(adapterNavFolder);
boolean nav_folder = prefs.getBoolean("nav_folder", true);
ibExpanderFolder.setImageLevel(nav_folder ? 0 /* less */ : 1 /* more */);
rvFolder.setVisibility(nav_folder ? View.VISIBLE : View.GONE);
ibExpanderFolder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean nav_folder = !prefs.getBoolean("nav_folder", true);
prefs.edit().putBoolean("nav_folder", nav_folder).apply();
ibExpanderFolder.setImageLevel(nav_folder ? 0 /* less */ : 1 /* more */);
rvFolder.setVisibility(nav_folder ? View.VISIBLE : View.GONE);
}
});
// Menus
rvMenu.setLayoutManager(new LinearLayoutManager(this));
adapterNavMenu = new AdapterNavMenu(this, this);
@ -754,9 +736,9 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
DB db = DB.getInstance(this);
db.account().liveAccountsEx(false).observe(owner, new Observer<List<TupleAccountEx>>() {
db.account().liveAccountFolder().observe(owner, 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<>();
adapterNavAccount.set(accounts, nav_expanded);
@ -772,17 +754,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
}
});
ibExpanderFolder.setVisibility(View.GONE);
db.folder().liveNavigation().observe(owner, new Observer<List<TupleFolderNav>>() {
@Override
public void onChanged(List<TupleFolderNav> folders) {
if (folders == null)
folders = new ArrayList<>();
ibExpanderFolder.setVisibility(folders.size() > 0 ? View.VISIBLE : View.GONE);
adapterNavFolder.set(folders, nav_expanded);
}
});
db.operation().liveStats().observe(owner, new Observer<TupleOperationStats>() {
@Override
public void onChanged(TupleOperationStats stats) {
@ -904,7 +875,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
adapterNavAccount.setExpanded(nav_expanded);
adapterNavUnified.setExpanded(nav_expanded);
adapterNavFolder.setExpanded(nav_expanded);
adapterNavMenu.setExpanded(nav_expanded);
adapterNavMenuExtra.setExpanded(nav_expanded);
}

@ -41,25 +41,31 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback;
import androidx.recyclerview.widget.RecyclerView;
import java.text.Collator;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
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 AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.ViewHolder> {
public class AdapterNavAccountFolder extends RecyclerView.Adapter<AdapterNavAccountFolder.ViewHolder> {
private Context context;
private LifecycleOwner owner;
private LayoutInflater inflater;
private boolean nav_count;
private int dp6;
private int dp12;
private int colorUnread;
private int textColorSecondary;
private int colorWarning;
private boolean expanded = true;
private List<TupleAccountEx> items = new ArrayList<>();
private List<TupleAccountFolder> items = new ArrayList<>();
private NumberFormat NF = NumberFormat.getNumberInstance();
private DateFormat TF;
@ -99,44 +105,72 @@ public class AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.Vi
ivWarning.setOnClickListener(null);
}
private void bindTo(TupleAccountEx account) {
if ("connected".equals(account.state))
ivItem.setImageResource(account.primary
? R.drawable.twotone_folder_special_24
: R.drawable.twotone_folder_done_24);
private void bindTo(TupleAccountFolder account) {
int start = (account.folderName == null ? 0 : (expanded ? dp12 : dp6));
view.setPaddingRelative(start, 0, 0, 0);
if (account.folderName == null) {
if ("connected".equals(account.state))
ivItem.setImageResource(account.primary
? R.drawable.twotone_folder_special_24
: R.drawable.twotone_folder_done_24);
else
ivItem.setImageResource(account.backoff_until == null
? R.drawable.twotone_folder_24
: R.drawable.twotone_update_24);
} else {
if ("syncing".equals(account.folderSyncState))
ivItem.setImageResource(R.drawable.twotone_compare_arrows_24);
else if ("downloading".equals(account.folderSyncState))
ivItem.setImageResource(R.drawable.twotone_cloud_download_24);
else if (account.executing > 0)
ivItem.setImageResource(R.drawable.twotone_dns_24);
else
ivItem.setImageResource("connected".equals(account.folderState)
? R.drawable.twotone_folder_done_24
: R.drawable.twotone_folder_24);
}
int count;
if (EntityFolder.DRAFTS.equals(account.folderType))
count = account.messages;
else
ivItem.setImageResource(account.backoff_until == null
? R.drawable.twotone_folder_24
: R.drawable.twotone_update_24);
count = account.unseen;
if (account.color == null)
Integer color = (account.folderName == null ? account.color : account.folderColor);
if (color == null || !ActivityBilling.isPro(context))
ivItem.clearColorFilter();
else
ivItem.setColorFilter(account.color);
ivBadge.setVisibility(account.unseen == 0 || expanded ? View.GONE : View.VISIBLE);
ivItem.setColorFilter(color);
ivBadge.setVisibility(count == 0 || expanded ? View.GONE : View.VISIBLE);
if (account.unseen == 0)
tvItem.setText(account.name);
String name = account.getName(context);
if (count == 0)
tvItem.setText(name);
else
tvItem.setText(context.getString(R.string.title_name_count,
account.name, NF.format(account.unseen)));
tvItem.setText(context.getString(R.string.title_name_count, name, NF.format(count)));
tvItem.setTextColor(account.unseen == 0 ? textColorSecondary : colorUnread);
tvItem.setTypeface(account.unseen == 0 ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD);
tvItem.setTextColor(count == 0 ? textColorSecondary : colorUnread);
tvItem.setTypeface(count == 0 ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD);
tvItem.setVisibility(expanded ? View.VISIBLE : View.GONE);
tvItemExtra.setText(account.last_connected == null ? null : TF.format(account.last_connected));
tvItemExtra.setVisibility(account.last_connected != null && expanded ? View.VISIBLE : View.GONE);
if (account.folderName == null) {
tvItemExtra.setText(account.last_connected == null ? null : TF.format(account.last_connected));
tvItemExtra.setVisibility(account.last_connected != null && expanded ? View.VISIBLE : View.GONE);
} else {
tvItemExtra.setText(NF.format(account.messages));
tvItemExtra.setVisibility(nav_count && expanded ? View.VISIBLE : View.GONE);
}
ivExtra.setVisibility(View.GONE);
Integer percent = account.getQuotaPercentage();
if (account.error != null) {
if (account.error != null && account.folderName == null) {
ivWarning.setImageResource(R.drawable.twotone_warning_24);
ivWarning.setVisibility(expanded ? View.VISIBLE : View.GONE);
view.setBackgroundColor(expanded ? Color.TRANSPARENT : colorWarning);
} else if (percent != null && percent > QUOTA_WARNING) {
} else if (percent != null && percent > QUOTA_WARNING && account.folderName == null) {
ivWarning.setImageResource(R.drawable.twotone_disc_full_24);
ivWarning.setVisibility(expanded ? View.VISIBLE : View.GONE);
view.setBackgroundColor(expanded ? Color.TRANSPARENT : colorWarning);
@ -152,19 +186,28 @@ public class AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.Vi
if (pos == RecyclerView.NO_POSITION)
return;
TupleAccountEx account = items.get(pos);
TupleAccountFolder account = items.get(pos);
if (account == null)
return;
if (v.getId() == R.id.ivWarning && account.error == null) {
ToastEx.makeText(context, R.string.title_legend_quota, Toast.LENGTH_LONG).show();
return;
}
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_FOLDERS)
.putExtra("id", account.id));
if (account.folderName != null)
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_MESSAGES)
.putExtra("account", account.id)
.putExtra("folder", account.folderId)
.putExtra("type", account.folderType));
else {
if (v.getId() == R.id.ivWarning && account.error == null) {
ToastEx.makeText(context, R.string.title_legend_quota, Toast.LENGTH_LONG).show();
return;
}
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_FOLDERS)
.putExtra("id", account.id));
}
}
@Override
@ -173,8 +216,8 @@ public class AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.Vi
if (pos == RecyclerView.NO_POSITION)
return false;
TupleAccountEx account = items.get(pos);
if (account == null)
TupleAccountFolder account = items.get(pos);
if (account == null || account.folderName != null)
return false;
Bundle args = new Bundle();
@ -212,28 +255,78 @@ public class AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.Vi
}
}
AdapterNavAccount(Context context, LifecycleOwner owner) {
AdapterNavAccountFolder(Context context, LifecycleOwner owner) {
this.context = context;
this.owner = owner;
this.inflater = LayoutInflater.from(context);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
this.nav_count = prefs.getBoolean("nav_count", false);
boolean highlight_unread = prefs.getBoolean("highlight_unread", true);
int colorHighlight = prefs.getInt("highlight_color", Helper.resolveColor(context, R.attr.colorUnreadHighlight));
this.dp6 = Helper.dp2pixels(context, 6);
this.dp12 = Helper.dp2pixels(context, 12);
this.colorUnread = (highlight_unread ? colorHighlight : Helper.resolveColor(context, R.attr.colorUnread));
this.textColorSecondary = Helper.resolveColor(context, android.R.attr.textColorSecondary);
this.colorWarning = ColorUtils.setAlphaComponent(Helper.resolveColor(context, R.attr.colorWarning), 128);
this.TF = Helper.getTimeInstance(context, SimpleDateFormat.SHORT);
setHasStableIds(true);
setHasStableIds(false);
}
public void set(@NonNull List<TupleAccountEx> accounts, boolean expanded) {
public void set(@NonNull List<TupleAccountFolder> accounts, boolean expanded) {
Log.i("Set nav accounts=" + accounts.size());
if (accounts.size() > 0)
Collections.sort(accounts, accounts.get(0).getComparator(context));
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) {
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;
if (a1.folderName == null && a2.folderName == null)
return 0;
else if (a1.folderName == null)
return -1;
else if (a2.folderName == null)
return 1;
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;
return collator.compare(a1.getName(context), a2.getName(context));
}
});
}
DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, accounts), false);
@ -270,10 +363,10 @@ public class AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.Vi
}
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);
}
@ -290,22 +383,38 @@ public class AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.Vi
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
TupleAccountEx a1 = prev.get(oldItemPosition);
TupleAccountEx a2 = next.get(newItemPosition);
return a1.id.equals(a2.id);
TupleAccountFolder a1 = prev.get(oldItemPosition);
TupleAccountFolder a2 = next.get(newItemPosition);
return a1.id.equals(a2.id) &&
Objects.equals(a1.folderId, a2.folderId);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
TupleAccountEx a1 = prev.get(oldItemPosition);
TupleAccountEx a2 = next.get(newItemPosition);
return Objects.equals(a1.name, a2.name) &&
Objects.equals(a1.color, a2.color) &&
TupleAccountFolder a1 = prev.get(oldItemPosition);
TupleAccountFolder a2 = next.get(newItemPosition);
return Objects.equals(a1.order, a2.order) &&
a1.primary == a2.primary &&
a1.unseen == a2.unseen &&
Objects.equals(a1.state, a2.state) &&
Objects.equals(a1.last_connected, a2.last_connected) &&
Objects.equals(a1.error, a2.error);
Objects.equals(a1.name, a2.name) &&
Objects.equals(a1.color, a2.color) &&
Objects.equals(a1.folderId == null ? a1.state : null, a2.folderId == null ? a2.state : null) &&
Objects.equals(a1.folderId == null ? a1.last_connected : null, a2.folderId == null ? a2.last_connected : null) &&
Objects.equals(a1.folderId == null ? a1.error : null, a2.folderId == null ? a2.error : null) &&
Objects.equals(a1.folderId, a2.folderId) &&
Objects.equals(a1.folderType, a2.folderType) &&
Objects.equals(a1.folderOrder, a2.folderOrder) &&
Objects.equals(a1.folderName, a2.folderName) &&
Objects.equals(a1.folderDisplay, a2.folderDisplay) &&
Objects.equals(a1.folderColor, a2.folderColor) &&
Objects.equals(a1.folderSync, a2.folderSync) &&
Objects.equals(a1.folderState, a2.folderState) &&
Objects.equals(a1.folderSyncState, a2.folderSyncState) &&
a1.executing == a2.executing &&
a1.messages == a2.messages &&
a1.unseen == a2.unseen;
}
}
@ -328,7 +437,7 @@ public class AdapterNavAccount extends RecyclerView.Adapter<AdapterNavAccount.Vi
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.unwire();
TupleAccountEx account = items.get(position);
TupleAccountFolder account = items.get(position);
holder.bindTo(account);
holder.wire();
}

@ -1,281 +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-2021 by Marcel Bokhorst (M66B)
*/
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import androidx.lifecycle.LifecycleOwner;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback;
import androidx.recyclerview.widget.RecyclerView;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class AdapterNavFolder extends RecyclerView.Adapter<AdapterNavFolder.ViewHolder> {
private Context context;
private LifecycleOwner owner;
private LayoutInflater inflater;
private boolean nav_count;
private int colorUnread;
private int textColorSecondary;
private int colorWarning;
private boolean expanded = true;
private List<TupleFolderNav> items = new ArrayList<>();
private NumberFormat NF = NumberFormat.getNumberInstance();
private DateFormat TF;
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private View view;
private ImageView ivItem;
private ImageView ivBadge;
private TextView tvItem;
private TextView tvItemExtra;
private ImageView ivExtra;
private ImageView ivWarning;
ViewHolder(View itemView) {
super(itemView);
view = itemView.findViewById(R.id.clItem);
ivItem = itemView.findViewById(R.id.ivItem);
ivBadge = itemView.findViewById(R.id.ivBadge);
tvItem = itemView.findViewById(R.id.tvItem);
tvItemExtra = itemView.findViewById(R.id.tvItemExtra);
ivExtra = itemView.findViewById(R.id.ivExtra);
ivWarning = itemView.findViewById(R.id.ivWarning);
}
private void wire() {
view.setOnClickListener(this);
}
private void unwire() {
view.setOnClickListener(null);
}
private void bindTo(TupleFolderNav folder) {
if (EntityFolder.OUTBOX.equals(folder.type)) {
if ("syncing".equals(folder.sync_state))
ivItem.setImageResource(R.drawable.twotone_compare_arrows_24);
else
ivItem.setImageResource(EntityFolder.getIcon(folder.type));
ivItem.clearColorFilter();
} else {
if ("syncing".equals(folder.sync_state))
ivItem.setImageResource(R.drawable.twotone_compare_arrows_24);
else if ("downloading".equals(folder.sync_state))
ivItem.setImageResource(R.drawable.twotone_cloud_download_24);
else if (folder.executing > 0)
ivItem.setImageResource(R.drawable.twotone_dns_24);
else
ivItem.setImageResource("connected".equals(folder.state)
? R.drawable.twotone_folder_done_24
: R.drawable.twotone_folder_24);
if (folder.accountColor == null || !ActivityBilling.isPro(context))
ivItem.clearColorFilter();
else
ivItem.setColorFilter(folder.accountColor);
}
int count;
if (EntityFolder.DRAFTS.equals(folder.type) ||
EntityFolder.OUTBOX.equals(folder.type))
count = folder.messages;
else
count = folder.unseen;
ivBadge.setVisibility(count == 0 || expanded ? View.GONE : View.VISIBLE);
if (count == 0)
tvItem.setText(folder.getDisplayName(context));
else
tvItem.setText(context.getString(R.string.title_name_count,
folder.getDisplayName(context), NF.format(count)));
tvItem.setTextColor(count == 0 ? textColorSecondary : colorUnread);
tvItem.setTypeface(count == 0 ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD);
tvItem.setVisibility(expanded ? View.VISIBLE : View.GONE);
tvItemExtra.setText(NF.format(folder.messages));
tvItemExtra.setVisibility(nav_count && expanded ? View.VISIBLE : View.GONE);
ivExtra.setVisibility(View.GONE);
ivWarning.setVisibility(folder.error != null && expanded ? View.VISIBLE : View.GONE);
view.setBackgroundColor(folder.error != null && !expanded ? colorWarning : Color.TRANSPARENT);
}
@Override
public void onClick(View v) {
int pos = getAdapterPosition();
if (pos == RecyclerView.NO_POSITION)
return;
TupleFolderNav folder = items.get(pos);
if (folder == null)
return;
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_MESSAGES)
.putExtra("account", folder.account)
.putExtra("folder", folder.id)
.putExtra("type", folder.type));
}
}
AdapterNavFolder(Context context, LifecycleOwner owner) {
this.context = context;
this.owner = owner;
this.inflater = LayoutInflater.from(context);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
this.nav_count = prefs.getBoolean("nav_count", false);
boolean highlight_unread = prefs.getBoolean("highlight_unread", true);
int colorHighlight = prefs.getInt("highlight_color", Helper.resolveColor(context, R.attr.colorUnreadHighlight));
this.colorUnread = (highlight_unread ? colorHighlight : Helper.resolveColor(context, R.attr.colorUnread));
this.textColorSecondary = Helper.resolveColor(context, android.R.attr.textColorSecondary);
this.colorWarning = ColorUtils.setAlphaComponent(Helper.resolveColor(context, R.attr.colorWarning), 128);
this.TF = Helper.getTimeInstance(context, SimpleDateFormat.SHORT);
setHasStableIds(true);
}
public void set(@NonNull List<TupleFolderNav> folders, boolean expanded) {
Log.i("Set nav folders=" + folders.size() + " expanded=" + expanded);
if (folders.size() > 0)
Collections.sort(folders, folders.get(0).getComparator(context));
DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, folders), false);
this.expanded = expanded;
this.items = folders;
diff.dispatchUpdatesTo(new ListUpdateCallback() {
@Override
public void onInserted(int position, int count) {
Log.d("Inserted @" + position + " #" + count);
}
@Override
public void onRemoved(int position, int count) {
Log.d("Removed @" + position + " #" + count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
Log.d("Moved " + fromPosition + ">" + toPosition);
}
@Override
public void onChanged(int position, int count, Object payload) {
Log.d("Changed @" + position + " #" + count);
}
});
diff.dispatchUpdatesTo(this);
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
notifyDataSetChanged();
}
private static class DiffCallback extends DiffUtil.Callback {
private List<TupleFolderNav> prev = new ArrayList<>();
private List<TupleFolderNav> next = new ArrayList<>();
DiffCallback(List<TupleFolderNav> prev, List<TupleFolderNav> next) {
this.prev.addAll(prev);
this.next.addAll(next);
}
@Override
public int getOldListSize() {
return prev.size();
}
@Override
public int getNewListSize() {
return next.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
TupleFolderNav f1 = prev.get(oldItemPosition);
TupleFolderNav f2 = next.get(newItemPosition);
return f1.id.equals(f2.id);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
TupleFolderNav f1 = prev.get(oldItemPosition);
TupleFolderNav f2 = next.get(newItemPosition);
return f1.equals(f2);
}
}
@Override
public long getItemId(int position) {
return items.get(position).id;
}
@Override
public int getItemCount() {
return items.size();
}
@Override
@NonNull
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(inflater.inflate(R.layout.item_nav, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.unwire();
TupleFolderNav folder = items.get(position);
holder.bindTo(folder);
holder.wire();
}
}

@ -77,6 +77,55 @@ public interface DaoAccount {
", account.name COLLATE NOCASE")
LiveData<List<TupleAccountEx>> liveAccountsEx(boolean all);
@Query("SELECT account.*" +
", NULL AS folderId, NULL AS folderType, -1 AS folderOrder" +
", NULL AS folderName, NULL AS folderDisplay, NULL AS folderColor" +
", 0 AS folderSync, NULL AS folderState, NULL AS folderSyncState" +
", 0 AS executing" +
", 0 AS messages" +
", (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.ARCHIVE + "'" +
" AND folder.type <> '" + EntityFolder.TRASH + "'" +
" AND folder.type <> '" + EntityFolder.JUNK + "'" +
" AND folder.type <> '" + EntityFolder.DRAFTS + "'" +
" AND folder.type <> '" + EntityFolder.OUTBOX + "'" +
" AND NOT ui_seen" +
" AND NOT ui_hide) AS unseen" +
" FROM account" +
" WHERE account.synchronize" +
" UNION " +
" SELECT account.*" +
", folder.id AS folderId, folder.type AS folderType, folder.`order` AS folderOrder" +
", folder.name AS folderName, folder.display AS folderDisplay, folder.color AS folderColor" +
", folder.synchronize AS folderSync, folder.state AS foldeState, folder.sync_state AS folderSyncState" +
", (SELECT COUNT(operation.id) FROM operation" +
" WHERE operation.folder = folder.id" +
" AND state = 'executing') AS executing" +
", (SELECT COUNT(message.id) FROM message" +
" WHERE message.folder = folder.id" +
" AND NOT ui_hide) AS messages" +
", (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" +
" WHERE message.folder = folder.id" +
" AND NOT ui_seen" +
" AND NOT ui_hide) AS unseen" +
" FROM account" +
" JOIN folder ON folder.account = account.id" +
" WHERE account.synchronize" +
" AND folder.navigation")
LiveData<List<TupleAccountFolder>> liveAccountFolder();
@Query("SELECT account.*" +
", SUM(folder.synchronize) AS folders" +
", (SELECT COUNT(id) FROM operation" +

@ -116,19 +116,6 @@ public interface DaoFolder {
" GROUP BY folder.id")
LiveData<List<TupleFolderEx>> liveUnified(String type);
@Query("SELECT folder.*" +
", account.`order` AS accountOrder, account.name AS accountName, COALESCE(folder.color, account.color) AS accountColor" +
", COUNT(message.id) AS messages" +
", SUM(CASE WHEN NOT message.ui_seen THEN 1 ELSE 0 END) AS unseen" +
", (SELECT COUNT(operation.id) FROM operation WHERE operation.folder = folder.id) AS operations" +
", (SELECT COUNT(operation.id) FROM operation WHERE operation.folder = folder.id AND operation.state = 'executing') AS executing" +
" FROM folder" +
" LEFT JOIN account ON account.id = folder.account" +
" LEFT JOIN message ON message.folder = folder.id AND NOT message.ui_hide" +
" WHERE (account.`synchronize` AND folder.navigation)" +
" GROUP BY folder.id")
LiveData<List<TupleFolderNav>> liveNavigation();
@Query("SELECT account, id AS folder, unified, sync_state FROM folder" +
" WHERE sync_state IS NOT NULL" +
" AND folder.type <> '" + EntityFolder.OUTBOX + "'")

@ -0,0 +1,80 @@
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-2021 by Marcel Bokhorst (M66B)
*/
import android.content.Context;
import java.util.Objects;
public class TupleAccountFolder extends EntityAccount {
public Long folderId;
public String folderType;
public Integer folderOrder;
public String folderName;
public String folderDisplay;
public Integer folderColor;
public boolean folderSync;
public String folderState;
public String folderSyncState;
public int executing;
public int messages;
public int unseen;
public String getName(Context context) {
if (folderName == null)
return name; // account name
if (folderDisplay != null)
return folderDisplay;
if (EntityFolder.INBOX.equals(folderType))
return EntityFolder.localizeName(context, folderName);
if (separator == null)
return folderName;
int s = folderName.lastIndexOf(separator);
if (s < 0)
return folderName;
return folderName.substring(s + 1);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TupleAccountFolder) {
TupleAccountFolder other = (TupleAccountFolder) obj;
return (super.equals(obj) &&
Objects.equals(this.folderId, other.folderId) &&
Objects.equals(this.folderType, other.folderType) &&
Objects.equals(this.folderOrder, other.folderOrder) &&
Objects.equals(this.folderName, other.folderName) &&
Objects.equals(this.folderDisplay, other.folderDisplay) &&
Objects.equals(this.folderColor, other.folderColor) &&
this.folderSync == other.folderSync &&
Objects.equals(this.folderState, other.folderState) &&
Objects.equals(this.folderSyncState, other.folderSyncState) &&
this.executing == other.executing &&
this.messages == other.messages &&
this.unseen == other.unseen);
} else
return false;
}
}

@ -158,36 +158,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rvUnified" />
<ImageButton
android:id="@+id/ibExpanderFolder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/title_legend_expander"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/vSeparatorUnified"
app:srcCompat="@drawable/expander" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFolder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:animateLayoutChanges="false"
android:nestedScrollingEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ibExpanderFolder" />
<View
android:id="@+id/vSeparatorFolder"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?attr/colorSeparator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rvFolder" />
<ImageButton
android:id="@+id/ibExpanderMenu"
android:layout_width="0dp"
@ -196,7 +166,7 @@
android:contentDescription="@string/title_legend_expander"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/vSeparatorFolder"
app:layout_constraintTop_toBottomOf="@id/vSeparatorUnified"
app:srcCompat="@drawable/expander" />
<androidx.recyclerview.widget.RecyclerView

Loading…
Cancel
Save