Moved ordering to setup

This commit is contained in:
M66B 2019-05-01 07:58:45 +02:00
parent a9bc7ad84b
commit bbe2c76ee8
20 changed files with 692 additions and 231 deletions

View File

@ -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);

View File

@ -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<AdapterAccount.ViewHolder> {
@ -257,51 +255,6 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
return items.size();
}
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);
List<Long> 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<Void>() {
@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) {

View File

@ -133,7 +133,11 @@ public class AdapterNavFolder extends RecyclerView.Adapter<AdapterNavFolder.View
Collections.sort(folders, new Comparator<TupleFolderNav>() {
@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;

View File

@ -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 <http://www.gnu.org/licenses/>.
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<AdapterOrder.ViewHolder> {
private Context context;
private LifecycleOwner owner;
private LayoutInflater inflater;
private String clazz;
private List<EntityOrder> 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<EntityOrder> 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<EntityOrder>() {
@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<EntityOrder> prev = new ArrayList<>();
private List<EntityOrder> next = new ArrayList<>();
DiffCallback(List<EntityOrder> prev, List<EntityOrder> 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<EntityOrder> 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);
}
}

View File

@ -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<List<TupleAccountEx>> liveAccountsEx(boolean all);
@Query("SELECT * FROM account WHERE id = :id")

View File

@ -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<List<TupleFolderSort>> liveSort();
@Query("SELECT folder.*" +
", account.name AS accountName, account.color AS accountColor, account.state AS accountState" +
", COUNT(message.id) AS messages" +

View File

@ -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);

View File

@ -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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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);
}

View File

@ -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<Void>() {
@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) {
}
};
}

View File

@ -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<Void>() {
@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) {
}
});
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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<List<EntityAccount>>() {
@Override
public void onChanged(List<EntityAccount> accounts) {
if (accounts == null)
accounts = new ArrayList<>();
Log.i("Order " + clazz + "=" + accounts.size());
adapter.set((List<EntityOrder>) (List<?>) accounts);
pbWait.setVisibility(View.GONE);
grpReady.setVisibility(View.VISIBLE);
}
});
else if (TupleFolderSort.class.getName().equals(clazz))
db.folder().liveSort().observe(getViewLifecycleOwner(), new Observer<List<TupleFolderSort>>() {
@Override
public void onChanged(List<TupleFolderSort> folders) {
if (folders == null)
folders = new ArrayList<>();
Log.i("Order " + clazz + "=" + folders.size());
adapter.set((List<EntityOrder>) (List<?>) folders);
pbWait.setVisibility(View.GONE);
grpReady.setVisibility(View.VISIBLE);
}
});
else
throw new IllegalArgumentException();
}
@Override
public void onPause() {
super.onPause();
List<EntityOrder> items = adapter.getItems();
List<Long> 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<Void>() {
@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<Void>() {
@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) {
}
};
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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);
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,15h18v-2L3,13v2zM3,19h18v-2L3,17v2zM3,11h18L21,9L3,9v2zM3,5v2h18L21,5L3,5z"/>
</vector>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ActivitySetup">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvOrder"
android:layout_width="0dp"
android:layout_height="0dp"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<eu.faircode.email.ContentLoadingProgressBar
android:id="@+id/pbWait"
style="@style/Base.Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Group
android:id="@+id/grpReady"
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="rvOrder" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/clItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:padding="12dp">
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text1"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -8,9 +8,4 @@
android:title="@string/title_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="collapseActionView|always" />
<item
android:id="@+id/menu_reset_order"
android:title="@string/title_reset_order"
app:showAsAction="never" />
</menu>

View File

@ -14,15 +14,4 @@
android:checkable="true"
android:title="@string/title_show_folders"
app:showAsAction="never" />
<item
android:id="@+id/menu_reorder"
android:checkable="true"
android:title="@string/title_reorder"
app:showAsAction="never" />
<item
android:id="@+id/menu_reset_order"
android:title="@string/title_reset_order"
app:showAsAction="never" />
</menu>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_reset_order"
android:title="@string/title_reset_order"
app:showAsAction="never" />
</menu>

View File

@ -98,16 +98,6 @@
<string name="title_setup">Setup</string>
<string name="title_setup_help">Help</string>
<string name="title_setup_export">Export settings</string>
<string name="title_setup_import">Import settings</string>
<string name="title_setup_import_do">Imported accounts will be added, not overwritten</string>
<string name="title_setup_password">Password</string>
<string name="title_setup_password_repeat">Repeat password</string>
<string name="title_setup_password_missing">Password missing</string>
<string name="title_setup_password_different">Passwords different</string>
<string name="title_setup_password_invalid">Password invalid</string>
<string name="title_setup_exported">Settings exported</string>
<string name="title_setup_imported">Settings imported</string>
<string name="title_setup_quick">Quick setup</string>
<string name="title_setup_quick_remark">To quickly setup an account and an identity for most providers</string>
<string name="title_setup_quick_hint">The quick setup will fetch configuration information from autoconfig.thunderbird.net</string>
@ -132,14 +122,31 @@
<string name="title_setup_doze_instructions">In the next dialog, select \"All apps\" at the top, select this app and select and confirm \"Don\'t optimize\"</string>
<string name="title_setup_data">Disable data saving</string>
<string name="title_setup_inbox">Read messages</string>
<string name="title_setup_notifications">Manage notifications</string>
<string name="title_setup_to_do">To do</string>
<string name="title_setup_done">Done</string>
<string name="title_setup_export">Export settings</string>
<string name="title_setup_import">Import settings</string>
<string name="title_setup_import_do">Imported accounts will be added, not overwritten</string>
<string name="title_setup_password">Password</string>
<string name="title_setup_password_repeat">Repeat password</string>
<string name="title_setup_password_missing">Password missing</string>
<string name="title_setup_password_different">Passwords different</string>
<string name="title_setup_password_invalid">Password invalid</string>
<string name="title_setup_exported">Settings exported</string>
<string name="title_setup_imported">Settings imported</string>
<string name="title_setup_notifications">Manage notifications</string>
<string name="title_setup_reorder_accounts">Order accounts</string>
<string name="title_setup_reorder_folders">Order folders</string>
<string name="title_reset_order">Reset order</string>
<string name="title_setup_theme">Select theme</string>
<string name="title_setup_light_theme">Light theme</string>
<string name="title_setup_dark_theme">Dark theme</string>
<string name="title_setup_black_theme">Black theme</string>
<string name="title_setup_system_theme">System theme</string>
<string name="title_setup_advanced">Advanced</string>
<string name="title_setup_defaults">Restore defaults</string>
<string name="title_setup_options">Options</string>
@ -267,7 +274,6 @@
<string name="title_use_ip">Use local IP address instead of host name</string>
<string name="title_authorize">Select account</string>
<string name="title_authorizing">Authorizing account &#8230;</string>
<string name="title_setup_advanced">Advanced</string>
<string name="title_synchronize_account">Synchronize (receive messages)</string>
<string name="title_synchronize_identity">Synchronize (send messages)</string>
<string name="title_primary_account">Primary (default account)</string>
@ -457,8 +463,6 @@
<string name="title_search_device">Search on device</string>
<string name="title_searching">Searching \'%1$s\'</string>
<string name="title_reset_order">Reset order</string>
<string name="title_sort_on">Sort on</string>
<string name="title_sort_on_time">Time</string>
<string name="title_sort_on_unread">Unread</string>
@ -613,7 +617,6 @@
<string name="title_report">Report</string>
<string name="title_fix">Fix</string>
<string name="title_enable">Enable</string>
<string name="title_reorder">Reorder</string>
<string name="title_no_ask_again">Do not ask this again</string>
<string name="title_no_body">No message text found</string>
<string name="title_no_charset">Unsupported encoding: %1$s</string>