diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index 7454ffbcb8..981970bbd0 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -173,14 +173,6 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On } }).setSeparated()); - menus.add(new NavMenuItem(R.drawable.baseline_palette_24, R.string.title_setup_theme, new Runnable() { - @Override - public void run() { - drawerLayout.closeDrawer(drawerContainer); - onMenuTheme(); - } - })); - if (getIntentNotifications(this).resolveActivity(pm) != null) menus.add(new NavMenuItem(R.drawable.baseline_notifications_24, R.string.title_setup_notifications, new Runnable() { @Override @@ -190,11 +182,27 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On } })); - menus.add(new NavMenuItem(R.drawable.baseline_settings_applications_24, R.string.title_setup_advanced, new Runnable() { + menus.add(new NavMenuItem(R.drawable.baseline_reorder_24, R.string.title_setup_reorder_accounts, new Runnable() { @Override public void run() { drawerLayout.closeDrawer(drawerContainer); - onMenuOptions(); + onMenuOrder(R.string.title_setup_reorder_accounts, EntityAccount.class); + } + })); + + menus.add(new NavMenuItem(R.drawable.baseline_reorder_24, R.string.title_setup_reorder_folders, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuOrder(R.string.title_setup_reorder_folders, TupleFolderSort.class); + } + })); + + menus.add(new NavMenuItem(R.drawable.baseline_palette_24, R.string.title_setup_theme, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuTheme(); } })); @@ -204,6 +212,14 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On drawerLayout.closeDrawer(drawerContainer); onMenuContacts(); } + })); + + menus.add(new NavMenuItem(R.drawable.baseline_settings_applications_24, R.string.title_setup_advanced, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuOptions(); + } }).setSeparated()); menus.add(new NavMenuItem(R.drawable.baseline_help_24, R.string.menu_legend, new Runnable() { @@ -342,10 +358,6 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On handleImport(data, this.password); } - private void onManageNotifications() { - startActivity(getIntentNotifications(this)); - } - private void onMenuExport() { if (Helper.isPro(this)) try { @@ -403,6 +415,26 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On .show(); } + private void onManageNotifications() { + startActivity(getIntentNotifications(this)); + } + + private void onMenuOrder(int title, Class clazz) { + if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) + getSupportFragmentManager().popBackStack("order", FragmentManager.POP_BACK_STACK_INCLUSIVE); + + Bundle args = new Bundle(); + args.putInt("title", title); + args.putString("class", clazz.getName()); + + FragmentOrder fragment = new FragmentOrder(); + fragment.setArguments(args); + + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("order"); + fragmentTransaction.commit(); + } + private void onMenuTheme() { View dview = LayoutInflater.from(this).inflate(R.layout.dialog_theme, null); final RadioGroup rgTheme = dview.findViewById(R.id.rgTheme); @@ -449,15 +481,6 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On .show(); } - private void onMenuOptions() { - if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) - getSupportFragmentManager().popBackStack("options", FragmentManager.POP_BACK_STACK_INCLUSIVE); - - FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); - fragmentTransaction.replace(R.id.content_frame, new FragmentOptions()).addToBackStack("options"); - fragmentTransaction.commit(); - } - private void onMenuContacts() { if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) getSupportFragmentManager().popBackStack("contacts", FragmentManager.POP_BACK_STACK_INCLUSIVE); @@ -467,6 +490,15 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On fragmentTransaction.commit(); } + private void onMenuOptions() { + if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) + getSupportFragmentManager().popBackStack("options", FragmentManager.POP_BACK_STACK_INCLUSIVE); + + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.content_frame, new FragmentOptions()).addToBackStack("options"); + fragmentTransaction.commit(); + } + private void onMenuLegend() { if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) getSupportFragmentManager().popBackStack("legend", FragmentManager.POP_BACK_STACK_INCLUSIVE); diff --git a/app/src/main/java/eu/faircode/email/AdapterAccount.java b/app/src/main/java/eu/faircode/email/AdapterAccount.java index fe2eb9b7ad..1f437874f1 100644 --- a/app/src/main/java/eu/faircode/email/AdapterAccount.java +++ b/app/src/main/java/eu/faircode/email/AdapterAccount.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.Typeface; -import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -42,7 +41,6 @@ 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 AdapterAccount extends RecyclerView.Adapter { @@ -257,51 +255,6 @@ public class AdapterAccount extends RecyclerView.Adapter= items.size() || - to < 0 || to >= items.size()) - return; - - if (from < to) - for (int i = from; i < to; i++) - Collections.swap(items, i, i + 1); - else - for (int i = from; i > to; i--) - Collections.swap(items, i, i - 1); - notifyItemMoved(from, to); - - List order = new ArrayList<>(); - for (int i = 0; i < items.size(); i++) - order.add(items.get(i).id); - - Bundle args = new Bundle(); - args.putLongArray("order", Helper.toLongArray(order)); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - final long[] order = args.getLongArray("order"); - - final DB db = DB.getInstance(context); - db.runInTransaction(new Runnable() { - @Override - public void run() { - for (int i = 0; i < order.length; i++) - db.account().setAccountOrder(order[i], i); - } - }); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(context, owner, ex); - - } - }.execute(context, owner, args, "accounts:order"); - } - @Override @NonNull public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { diff --git a/app/src/main/java/eu/faircode/email/AdapterNavFolder.java b/app/src/main/java/eu/faircode/email/AdapterNavFolder.java index 5ee04b67f7..2b6a02f242 100644 --- a/app/src/main/java/eu/faircode/email/AdapterNavFolder.java +++ b/app/src/main/java/eu/faircode/email/AdapterNavFolder.java @@ -133,7 +133,11 @@ public class AdapterNavFolder extends RecyclerView.Adapter() { @Override public int compare(TupleFolderNav f1, TupleFolderNav f2) { - int o = Boolean.compare(EntityFolder.OUTBOX.equals(f1.type), EntityFolder.OUTBOX.equals(f2.type)); + int s = Boolean.compare(EntityFolder.OUTBOX.equals(f1.type), EntityFolder.OUTBOX.equals(f2.type)); + if (s != 0) + return s; + + int o = Integer.compare(f1.order == null ? -1 : f1.order, f2.order == null ? -1 : f2.order); if (o != 0) return o; diff --git a/app/src/main/java/eu/faircode/email/AdapterOrder.java b/app/src/main/java/eu/faircode/email/AdapterOrder.java new file mode 100644 index 0000000000..52c84d2faf --- /dev/null +++ b/app/src/main/java/eu/faircode/email/AdapterOrder.java @@ -0,0 +1,194 @@ +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 . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListUpdateCallback; +import androidx.recyclerview.widget.RecyclerView; + +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 AdapterOrder extends RecyclerView.Adapter { + private Context context; + private LifecycleOwner owner; + private LayoutInflater inflater; + + private String clazz; + private List items = new ArrayList<>(); + + public class ViewHolder extends RecyclerView.ViewHolder { + private TextView tvTitle; + + ViewHolder(View itemView) { + super(itemView); + + tvTitle = itemView.findViewById(R.id.tvTitle); + } + + private void bindTo(EntityOrder item) { + tvTitle.setText(item.getSortTitle(context)); + } + } + + AdapterOrder(Context context, LifecycleOwner owner, String clazz) { + this.context = context; + this.owner = owner; + this.clazz = clazz; + this.inflater = LayoutInflater.from(context); + setHasStableIds(true); + } + + public void set(@NonNull List items) { + Log.i("Set sort items=" + items.size()); + + final Collator collator = Collator.getInstance(Locale.getDefault()); + collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc + + Collections.sort(items, new Comparator() { + @Override + public int compare(EntityOrder s1, EntityOrder s2) { + int o = Integer.compare(s1.order == null ? -1 : s1.order, s2.order == null ? -1 : s2.order); + if (o != 0) + return o; + + String name1 = s1.getSortTitle(context); + String name2 = s2.getSortTitle(context); + return collator.compare(name1, name2); + } + }); + + DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(this.items, items), false); + + this.items = items; + + diff.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + Log.i("Inserted @" + position + " #" + count); + } + + @Override + public void onRemoved(int position, int count) { + Log.i("Removed @" + position + " #" + count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + Log.i("Moved " + fromPosition + ">" + toPosition); + } + + @Override + public void onChanged(int position, int count, Object payload) { + Log.i("Changed @" + position + " #" + count); + } + }); + diff.dispatchUpdatesTo(this); + } + + private class DiffCallback extends DiffUtil.Callback { + private List prev = new ArrayList<>(); + private List next = new ArrayList<>(); + + DiffCallback(List prev, List 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) { + EntityOrder s1 = prev.get(oldItemPosition); + EntityOrder s2 = next.get(newItemPosition); + return s1.getSortId().equals(s2.getSortId()); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + EntityOrder s1 = prev.get(oldItemPosition); + EntityOrder s2 = next.get(newItemPosition); + return (Objects.equals(s1.order, s2.order)); + } + } + + @Override + public long getItemId(int position) { + return items.get(position).getSortId(); + } + + @Override + public int getItemCount() { + return items.size(); + } + + List getItems() { + return this.items; + } + + void onMove(int from, int to) { + if (from < 0 || from >= items.size() || + to < 0 || to >= items.size()) + return; + + if (from < to) + for (int i = from; i < to; i++) + Collections.swap(items, i, i + 1); + else + for (int i = from; i > to; i--) + Collections.swap(items, i, i - 1); + + notifyItemMoved(from, to); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.item_order, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + EntityOrder item = items.get(position); + holder.bindTo(item); + } +} + diff --git a/app/src/main/java/eu/faircode/email/DaoAccount.java b/app/src/main/java/eu/faircode/email/DaoAccount.java index 8b328932af..187a7c3633 100644 --- a/app/src/main/java/eu/faircode/email/DaoAccount.java +++ b/app/src/main/java/eu/faircode/email/DaoAccount.java @@ -63,7 +63,9 @@ public interface DaoAccount { " LEFT JOIN folder AS drafts ON drafts.account = account.id AND drafts.type = '" + EntityFolder.DRAFTS + "'" + " WHERE :all OR account.synchronize" + " GROUP BY account.id" + - " ORDER BY `order`, `primary` DESC, name COLLATE NOCASE") + " ORDER BY CASE WHEN :all THEN 0 ELSE account.`order` END" + + ", CASE WHEN :all THEN 0 ELSE account.`primary` END DESC" + + ", account.name COLLATE NOCASE") LiveData> liveAccountsEx(boolean all); @Query("SELECT * FROM account WHERE id = :id") diff --git a/app/src/main/java/eu/faircode/email/DaoFolder.java b/app/src/main/java/eu/faircode/email/DaoFolder.java index a91d4fa754..8ad27547bc 100644 --- a/app/src/main/java/eu/faircode/email/DaoFolder.java +++ b/app/src/main/java/eu/faircode/email/DaoFolder.java @@ -51,6 +51,13 @@ public interface DaoFolder { " AND (:search OR (account.synchronize AND account.browse))") EntityFolder getBrowsableFolder(long folder, boolean search); + @Query("SELECT folder.*" + + ", account.name AS accountName" + + " FROM folder" + + " JOIN account ON account.id = folder.account" + + " WHERE account.`synchronize`") + LiveData> liveSort(); + @Query("SELECT folder.*" + ", account.name AS accountName, account.color AS accountColor, account.state AS accountState" + ", COUNT(message.id) AS messages" + diff --git a/app/src/main/java/eu/faircode/email/EntityAccount.java b/app/src/main/java/eu/faircode/email/EntityAccount.java index c06db09e26..b0191c182d 100644 --- a/app/src/main/java/eu/faircode/email/EntityAccount.java +++ b/app/src/main/java/eu/faircode/email/EntityAccount.java @@ -41,7 +41,7 @@ import java.util.Objects; indices = { } ) -public class EntityAccount implements Serializable { +public class EntityAccount extends EntityOrder implements Serializable { static final String TABLE_NAME = "account"; static final int DEFAULT_KEEP_ALIVE_INTERVAL = 19; // minutes @@ -70,7 +70,6 @@ public class EntityAccount implements Serializable { public String name; public String signature; // obsolete public Integer color; - public Integer order; @NonNull public Boolean synchronize; @@ -119,6 +118,16 @@ public class EntityAccount implements Serializable { nm.deleteNotificationChannel(getNotificationChannelId(id)); } + @Override + Long getSortId() { + return id; + } + + @Override + String getSortTitle(Context context) { + return name; + } + public JSONObject toJSON() throws JSONException { JSONObject json = new JSONObject(); json.put("id", id); diff --git a/app/src/main/java/eu/faircode/email/EntityFolder.java b/app/src/main/java/eu/faircode/email/EntityFolder.java index 49165a52e9..e919451555 100644 --- a/app/src/main/java/eu/faircode/email/EntityFolder.java +++ b/app/src/main/java/eu/faircode/email/EntityFolder.java @@ -59,7 +59,7 @@ import static androidx.room.ForeignKey.CASCADE; } ) -public class EntityFolder implements Serializable { +public class EntityFolder extends EntityOrder implements Serializable { static final String TABLE_NAME = "folder"; @PrimaryKey(autoGenerate = true) @@ -84,7 +84,6 @@ public class EntityFolder implements Serializable { @NonNull public Integer keep_days; public String display; - public Integer order; @NonNull public Boolean hide = false; @NonNull @@ -227,6 +226,16 @@ public class EntityFolder implements Serializable { return (display == null ? Helper.localizeFolderName(context, n) : display); } + @Override + Long getSortId() { + return id; + } + + @Override + String getSortTitle(Context context) { + return Helper.localizeFolderName(context, name); + } + boolean isOutgoing() { return isOutgoing(this.type); } diff --git a/app/src/main/java/eu/faircode/email/EntityOrder.java b/app/src/main/java/eu/faircode/email/EntityOrder.java new file mode 100644 index 0000000000..b932a7b9fb --- /dev/null +++ b/app/src/main/java/eu/faircode/email/EntityOrder.java @@ -0,0 +1,30 @@ +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 . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; + +public abstract class EntityOrder { + public Integer order; + + abstract Long getSortId(); + + abstract String getSortTitle(Context context); +} diff --git a/app/src/main/java/eu/faircode/email/FragmentAccounts.java b/app/src/main/java/eu/faircode/email/FragmentAccounts.java index bcecfae309..4a3baf2906 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccounts.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccounts.java @@ -40,7 +40,6 @@ import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.Observer; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -95,7 +94,6 @@ public class FragmentAccounts extends FragmentBase { adapter = new AdapterAccount(getContext(), getViewLifecycleOwner(), settings); rvAccount.setAdapter(adapter); - new ItemTouchHelper(touchHelper).attachToRecyclerView(rvAccount); fab.setOnClickListener(new View.OnClickListener() { @Override @@ -256,59 +254,4 @@ public class FragmentAccounts extends FragmentBase { super.onPrepareOptionsMenu(menu); } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_reset_order: - onResetOrder(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private void onResetOrder() { - Bundle args = new Bundle(); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - DB db = DB.getInstance(context); - db.account().resetAccountOrder(); - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(this, args, "accounts:reset"); - } - - private ItemTouchHelper.Callback touchHelper = new ItemTouchHelper.Callback() { - @Override - public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { - int flags = 0; - int pos = viewHolder.getAdapterPosition(); - if (pos != RecyclerView.NO_POSITION) { - if (pos - 1 >= 0) - flags |= ItemTouchHelper.UP; - if (pos + 1 < rvAccount.getAdapter().getItemCount()) - flags |= ItemTouchHelper.DOWN; - } - - return makeMovementFlags(flags, 0); - } - - @Override - public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) { - ((AdapterAccount) rvAccount.getAdapter()).onMove(source.getAdapterPosition(), target.getAdapterPosition()); - return true; - } - - @Override - public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { - } - }; } diff --git a/app/src/main/java/eu/faircode/email/FragmentFolders.java b/app/src/main/java/eu/faircode/email/FragmentFolders.java index 7004b43fa9..fc80ef569f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolders.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolders.java @@ -43,7 +43,6 @@ import androidx.lifecycle.Observer; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -70,7 +69,6 @@ public class FragmentFolders extends FragmentBase { private long account; private boolean show_hidden = false; - private boolean reorder = false; private String searching = null; private AdapterFolder adapter; @@ -403,9 +401,6 @@ public class FragmentFolders extends FragmentBase { @Override public void onPrepareOptionsMenu(Menu menu) { menu.findItem(R.id.menu_show_hidden).setChecked(show_hidden); - menu.findItem(R.id.menu_reorder).setChecked(reorder); - menu.findItem(R.id.menu_reorder).setVisible(account < 0); // unified folders - menu.findItem(R.id.menu_reset_order).setVisible(account < 0); // unified folders super.onPrepareOptionsMenu(menu); } @@ -415,12 +410,6 @@ public class FragmentFolders extends FragmentBase { case R.id.menu_show_hidden: onMenuShowHidden(); return true; - case R.id.menu_reorder: - onReorder(); - return true; - case R.id.menu_reset_order: - onResetOrder(); - return true; default: return super.onOptionsItemSelected(item); } @@ -432,60 +421,4 @@ public class FragmentFolders extends FragmentBase { getActivity().invalidateOptionsMenu(); adapter.setShowHidden(show_hidden); } - - private void onReorder() { - reorder = !reorder; - getActivity().invalidateOptionsMenu(); - - swipeRefresh.setEnabled(!reorder); - adapter.setReorder(reorder); - if (reorder) - touchHelper.attachToRecyclerView(rvFolder); - else - touchHelper.attachToRecyclerView(null); - } - - private void onResetOrder() { - Bundle args = new Bundle(); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - DB db = DB.getInstance(context); - db.folder().resetFolderOrder(); - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(this, args, "folders:reset"); - } - - private ItemTouchHelper touchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() { - @Override - public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { - int flags = 0; - int pos = viewHolder.getAdapterPosition(); - if (pos != RecyclerView.NO_POSITION) { - if (pos - 1 >= 0) - flags |= ItemTouchHelper.UP; - if (pos + 1 < rvFolder.getAdapter().getItemCount()) - flags |= ItemTouchHelper.DOWN; - } - - return makeMovementFlags(flags, 0); - } - - @Override - public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) { - ((AdapterFolder) rvFolder.getAdapter()).onMove(source.getAdapterPosition(), target.getAdapterPosition()); - return true; - } - - @Override - public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { - } - }); } diff --git a/app/src/main/java/eu/faircode/email/FragmentOrder.java b/app/src/main/java/eu/faircode/email/FragmentOrder.java new file mode 100644 index 0000000000..def13445c0 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentOrder.java @@ -0,0 +1,247 @@ +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 . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.Group; +import androidx.lifecycle.Observer; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +public class FragmentOrder extends FragmentBase { + private int title; + private String clazz; + + private RecyclerView rvOrder; + private ContentLoadingProgressBar pbWait; + private Group grpReady; + + private AdapterOrder adapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Get arguments + Bundle args = getArguments(); + title = args.getInt("title", -1); + clazz = args.getString("class"); + Log.i("Order class=" + clazz); + } + + @Override + @Nullable + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + setSubtitle(title); + setHasOptionsMenu(true); + + View view = inflater.inflate(R.layout.fragment_order, container, false); + + // Get controls + rvOrder = view.findViewById(R.id.rvOrder); + pbWait = view.findViewById(R.id.pbWait); + grpReady = view.findViewById(R.id.grpReady); + + // Wire controls + rvOrder.setHasFixedSize(true); + LinearLayoutManager llm = new LinearLayoutManager(getContext()); + rvOrder.setLayoutManager(llm); + + adapter = new AdapterOrder(getContext(), getViewLifecycleOwner(), clazz); + rvOrder.setAdapter(adapter); + new ItemTouchHelper(touchHelper).attachToRecyclerView(rvOrder); + + // Initialize + grpReady.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); + + return view; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + DB db = DB.getInstance(getContext()); + + if (EntityAccount.class.getName().equals(clazz)) + db.account().liveSynchronizingAccounts().observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List accounts) { + if (accounts == null) + accounts = new ArrayList<>(); + + Log.i("Order " + clazz + "=" + accounts.size()); + + adapter.set((List) (List) accounts); + + pbWait.setVisibility(View.GONE); + grpReady.setVisibility(View.VISIBLE); + } + }); + else if (TupleFolderSort.class.getName().equals(clazz)) + db.folder().liveSort().observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List folders) { + if (folders == null) + folders = new ArrayList<>(); + + Log.i("Order " + clazz + "=" + folders.size()); + + adapter.set((List) (List) folders); + + pbWait.setVisibility(View.GONE); + grpReady.setVisibility(View.VISIBLE); + } + }); + else + throw new IllegalArgumentException(); + } + + @Override + public void onPause() { + super.onPause(); + + List items = adapter.getItems(); + + List order = new ArrayList<>(); + for (int i = 0; i < items.size(); i++) + order.add(items.get(i).getSortId()); + + Bundle args = new Bundle(); + args.putString("class", clazz); + args.putLongArray("order", Helper.toLongArray(order)); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + final String clazz = args.getString("class"); + final long[] order = args.getLongArray("order"); + + final DB db = DB.getInstance(context); + db.runInTransaction(new Runnable() { + @Override + public void run() { + for (int i = 0; i < order.length; i++) + if (EntityAccount.class.getName().equals(clazz)) + db.account().setAccountOrder(order[i], i); + else if (TupleFolderSort.class.getName().equals(clazz)) + db.folder().setFolderOrder(order[i], i); + else + throw new IllegalArgumentException("Unknown class=" + clazz); + } + }); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + + } + }.execute(getContext(), getViewLifecycleOwner(), args, "order:set"); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.menu_sort, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_reset_order: + onMenuResetOrder(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void onMenuResetOrder() { + Bundle args = new Bundle(); + args.putString("class", clazz); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + String clazz = args.getString("class"); + + DB db = DB.getInstance(context); + + if (EntityAccount.class.getName().equals(clazz)) + db.account().resetAccountOrder(); + else if (TupleFolderSort.class.getName().equals(clazz)) + db.folder().resetFolderOrder(); + else + throw new IllegalArgumentException("Unknown class=" + clazz); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(this, args, "order:reset"); + } + + private ItemTouchHelper.Callback touchHelper = new ItemTouchHelper.Callback() { + @Override + public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { + int flags = 0; + int pos = viewHolder.getAdapterPosition(); + if (pos != RecyclerView.NO_POSITION) { + if (pos - 1 >= 0) + flags |= ItemTouchHelper.UP; + if (pos + 1 < rvOrder.getAdapter().getItemCount()) + flags |= ItemTouchHelper.DOWN; + } + + return makeMovementFlags(flags, 0); + } + + @Override + public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) { + ((AdapterOrder) rvOrder.getAdapter()).onMove(source.getAdapterPosition(), target.getAdapterPosition()); + return true; + } + + @Override + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { + } + }; +} diff --git a/app/src/main/java/eu/faircode/email/TupleFolderSort.java b/app/src/main/java/eu/faircode/email/TupleFolderSort.java new file mode 100644 index 0000000000..ecf69e63ac --- /dev/null +++ b/app/src/main/java/eu/faircode/email/TupleFolderSort.java @@ -0,0 +1,31 @@ +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 . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; + +public class TupleFolderSort extends EntityFolder { + public String accountName; + + @Override + String getSortTitle(Context context) { + return accountName + "/" + super.getSortTitle(context); + } +} diff --git a/app/src/main/res/drawable/baseline_reorder_24.xml b/app/src/main/res/drawable/baseline_reorder_24.xml new file mode 100644 index 0000000000..28146620db --- /dev/null +++ b/app/src/main/res/drawable/baseline_reorder_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_order.xml b/app/src/main/res/layout/fragment_order.xml new file mode 100644 index 0000000000..a0ec0d81a0 --- /dev/null +++ b/app/src/main/res/layout/fragment_order.xml @@ -0,0 +1,36 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/item_order.xml b/app/src/main/res/layout/item_order.xml new file mode 100644 index 0000000000..799a7f8069 --- /dev/null +++ b/app/src/main/res/layout/item_order.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_accounts.xml b/app/src/main/res/menu/menu_accounts.xml index 61c10cf2cc..38f71a0ad1 100644 --- a/app/src/main/res/menu/menu_accounts.xml +++ b/app/src/main/res/menu/menu_accounts.xml @@ -8,9 +8,4 @@ android:title="@string/title_search" app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="collapseActionView|always" /> - - diff --git a/app/src/main/res/menu/menu_folders.xml b/app/src/main/res/menu/menu_folders.xml index 6a68c36741..b621a2ae0d 100644 --- a/app/src/main/res/menu/menu_folders.xml +++ b/app/src/main/res/menu/menu_folders.xml @@ -14,15 +14,4 @@ android:checkable="true" android:title="@string/title_show_folders" app:showAsAction="never" /> - - - - diff --git a/app/src/main/res/menu/menu_sort.xml b/app/src/main/res/menu/menu_sort.xml new file mode 100644 index 0000000000..0023e4cd51 --- /dev/null +++ b/app/src/main/res/menu/menu_sort.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7e7572dd19..1faac91fa7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -98,16 +98,6 @@ Setup Help - Export settings - Import settings - Imported accounts will be added, not overwritten - Password - Repeat password - Password missing - Passwords different - Password invalid - Settings exported - Settings imported Quick setup To quickly setup an account and an identity for most providers The quick setup will fetch configuration information from autoconfig.thunderbird.net @@ -132,14 +122,31 @@ In the next dialog, select \"All apps\" at the top, select this app and select and confirm \"Don\'t optimize\" Disable data saving Read messages - Manage notifications To do Done + + Export settings + Import settings + Imported accounts will be added, not overwritten + Password + Repeat password + Password missing + Passwords different + Password invalid + Settings exported + Settings imported + + Manage notifications + Order accounts + Order folders + Reset order Select theme Light theme Dark theme Black theme System theme + Advanced + Restore defaults Options @@ -267,7 +274,6 @@ Use local IP address instead of host name Select account Authorizing account … - Advanced Synchronize (receive messages) Synchronize (send messages) Primary (default account) @@ -457,8 +463,6 @@ Search on device Searching \'%1$s\' - Reset order - Sort on Time Unread @@ -613,7 +617,6 @@ Report Fix Enable - Reorder Do not ask this again No message text found Unsupported encoding: %1$s