Rule execution

pull/147/head
M66B 7 years ago
parent 699d1be7ec
commit 97c4d39a0f

@ -45,18 +45,20 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
private LifecycleOwner owner; private LifecycleOwner owner;
private LayoutInflater inflater; private LayoutInflater inflater;
private List<EntityRule> all = new ArrayList<>(); private List<TupleRuleEx> all = new ArrayList<>();
private List<EntityRule> filtered = new ArrayList<>(); private List<TupleRuleEx> filtered = new ArrayList<>();
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private View itemView; private View itemView;
private TextView tvName; private TextView tvName;
private TextView tvFolder;
ViewHolder(View itemView) { ViewHolder(View itemView) {
super(itemView); super(itemView);
this.itemView = itemView.findViewById(R.id.clItem); this.itemView = itemView.findViewById(R.id.clItem);
tvName = itemView.findViewById(R.id.tvName); tvName = itemView.findViewById(R.id.tvName);
tvFolder = itemView.findViewById(R.id.tvFolder);
} }
private void wire() { private void wire() {
@ -67,9 +69,10 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
itemView.setOnClickListener(null); itemView.setOnClickListener(null);
} }
private void bindTo(EntityRule rule) { private void bindTo(TupleRuleEx rule) {
itemView.setActivated(!rule.enabled); itemView.setActivated(!rule.enabled);
tvName.setText(rule.name); tvName.setText(rule.name);
tvFolder.setText(rule.folderName + "/" + rule.accountName);
} }
@Override @Override
@ -78,7 +81,7 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
if (pos == RecyclerView.NO_POSITION) if (pos == RecyclerView.NO_POSITION)
return; return;
EntityRule rule = filtered.get(pos); TupleRuleEx rule = filtered.get(pos);
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast( lbm.sendBroadcast(
@ -94,15 +97,24 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
setHasStableIds(true); setHasStableIds(true);
} }
public void set(@NonNull List<EntityRule> rules) { public void set(@NonNull List<TupleRuleEx> rules) {
Log.i("Set rules=" + rules.size()); Log.i("Set rules=" + rules.size());
final Collator collator = Collator.getInstance(Locale.getDefault()); final Collator collator = Collator.getInstance(Locale.getDefault());
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
Collections.sort(rules, new Comparator<EntityRule>() { Collections.sort(rules, new Comparator<TupleRuleEx>() {
@Override @Override
public int compare(EntityRule r1, EntityRule r2) { public int compare(TupleRuleEx r1, TupleRuleEx r2) {
int a = collator.compare(r1.accountName, r2.accountName);
if (a != 0)
return a;
int f = collator.compare(r1.folderName, r2.folderName);
if (f != 0)
return f;
int o = ((Integer) r1.order).compareTo(r2.order);
if (o != 0)
return 0;
return collator.compare(r1.name, r2.name); return collator.compare(r1.name, r2.name);
} }
}); });
@ -139,10 +151,10 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
} }
private class MessageDiffCallback extends DiffUtil.Callback { private class MessageDiffCallback extends DiffUtil.Callback {
private List<EntityRule> prev; private List<TupleRuleEx> prev;
private List<EntityRule> next; private List<TupleRuleEx> next;
MessageDiffCallback(List<EntityRule> prev, List<EntityRule> next) { MessageDiffCallback(List<TupleRuleEx> prev, List<TupleRuleEx> next) {
this.prev = prev; this.prev = prev;
this.next = next; this.next = next;
} }
@ -159,15 +171,15 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
@Override @Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
EntityRule r1 = prev.get(oldItemPosition); TupleRuleEx r1 = prev.get(oldItemPosition);
EntityRule r2 = next.get(newItemPosition); TupleRuleEx r2 = next.get(newItemPosition);
return r1.id.equals(r2.id); return r1.id.equals(r2.id);
} }
@Override @Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
EntityRule r1 = prev.get(oldItemPosition); TupleRuleEx r1 = prev.get(oldItemPosition);
EntityRule r2 = next.get(newItemPosition); TupleRuleEx r2 = next.get(newItemPosition);
return r1.equals(r2); return r1.equals(r2);
} }
} }
@ -191,7 +203,7 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.unwire(); holder.unwire();
EntityRule rule = filtered.get(position); TupleRuleEx rule = filtered.get(position);
holder.bindTo(rule); holder.bindTo(rule);
holder.wire(); holder.wire();
} }

@ -31,16 +31,20 @@ import androidx.room.Update;
public interface DaoRule { public interface DaoRule {
@Query("SELECT * FROM rule" + @Query("SELECT * FROM rule" +
" WHERE folder = :folder" + " WHERE folder = :folder" +
" AND enabled" +
" ORDER BY `order`") " ORDER BY `order`")
List<EntityRule> getRules(long folder); List<EntityRule> getEnabledRules(long folder);
@Query("SELECT rule.*, folder.account FROM rule" + @Query("SELECT rule.*, folder.account, folder.name AS folderName, account.name AS accountName FROM rule" +
" JOIN folder ON folder.id = rule.folder" + " JOIN folder ON folder.id = rule.folder" +
" JOIN account ON account.id = folder.account" +
" WHERE rule.id = :id") " WHERE rule.id = :id")
TupleRuleEx getRule(long id); TupleRuleEx getRule(long id);
@Query("SELECT * FROM rule ORDER BY `order`") @Query("SELECT rule.*, folder.account, folder.name AS folderName, account.name AS accountName FROM rule" +
LiveData<List<EntityRule>> liveRules(); " JOIN folder ON folder.id = rule.folder" +
" JOIN account ON account.id = folder.account")
LiveData<List<TupleRuleEx>> liveRules();
@Insert @Insert
long insertRule(EntityRule rule); long insertRule(EntityRule rule);

@ -67,12 +67,13 @@ public class EntityRule {
static final int TYPE_UNSEEN = 2; static final int TYPE_UNSEEN = 2;
static final int TYPE_MOVE = 3; static final int TYPE_MOVE = 3;
boolean matches(Context context, EntityMessage message) throws JSONException, IOException { boolean matches(Context context, EntityMessage message) throws IOException {
try {
JSONObject jcondition = new JSONObject(condition); JSONObject jcondition = new JSONObject(condition);
String sender = jcondition.getString("sender"); String sender = jcondition.optString("sender", null);
String subject = jcondition.getString("subject"); String subject = jcondition.optString("subject", null);
String text = jcondition.getString("text"); String text = jcondition.optString("text", null);
boolean regex = jcondition.getBoolean("regex"); boolean regex = jcondition.optBoolean("regex", false);
if (sender != null && message.from != null) { if (sender != null && message.from != null) {
if (matches(sender, MessageHelper.getFormattedAddresses(message.from, true), regex)) if (matches(sender, MessageHelper.getFormattedAddresses(message.from, true), regex))
@ -90,6 +91,9 @@ public class EntityRule {
if (matches(text, santized, regex)) if (matches(text, santized, regex))
return true; return true;
} }
} catch (JSONException ex) {
Log.e(ex);
}
return false; return false;
} }
@ -102,9 +106,13 @@ public class EntityRule {
return haystack.contains(needle); return haystack.contains(needle);
} }
void execute(Context context, DB db, EntityMessage message) throws JSONException { void execute(Context context, DB db, EntityMessage message) {
try {
JSONObject jargs = new JSONObject(action); JSONObject jargs = new JSONObject(action);
switch (jargs.getInt("type")) { int type = jargs.getInt("type");
Log.i("Executing rule=" + type + " message=" + message.id);
switch (type) {
case TYPE_SEEN: case TYPE_SEEN:
onActionSeen(context, db, message, true); onActionSeen(context, db, message, true);
break; break;
@ -115,17 +123,18 @@ public class EntityRule {
onActionMove(context, db, message, jargs); onActionMove(context, db, message, jargs);
break; break;
} }
} catch (JSONException ex) {
Log.e(ex);
}
} }
private void onActionSeen(Context context, DB db, EntityMessage message, boolean seen) { private void onActionSeen(Context context, DB db, EntityMessage message, boolean seen) {
EntityOperation.queue(context, db, message, EntityOperation.SEEN, seen); EntityOperation.queue(context, db, message, EntityOperation.SEEN, seen);
message.seen = seen;
} }
private void onActionMove(Context context, DB db, EntityMessage message, JSONObject jargs) throws JSONException { private void onActionMove(Context context, DB db, EntityMessage message, JSONObject jargs) throws JSONException {
long target = jargs.getLong("target"); long target = jargs.getLong("target");
boolean seen = jargs.getBoolean("seen");
if (seen)
EntityOperation.queue(context, db, message, EntityOperation.SEEN, true);
EntityOperation.queue(context, db, message, EntityOperation.MOVE, target); EntityOperation.queue(context, db, message, EntityOperation.MOVE, target);
} }

@ -58,8 +58,7 @@ public class FragmentRule extends FragmentBase {
private EditText etSubject; private EditText etSubject;
private EditText etText; private EditText etText;
private Spinner spAction; private Spinner spAction;
private Spinner spMove; private Spinner spTarget;
private CheckBox cbMoveSeen;
private BottomNavigationView bottom_navigation; private BottomNavigationView bottom_navigation;
private ContentLoadingProgressBar pbWait; private ContentLoadingProgressBar pbWait;
private Group grpReady; private Group grpReady;
@ -68,6 +67,7 @@ public class FragmentRule extends FragmentBase {
private ArrayAdapter<EntityAccount> adapterAccount; private ArrayAdapter<EntityAccount> adapterAccount;
private ArrayAdapter<EntityFolder> adapterFolder; private ArrayAdapter<EntityFolder> adapterFolder;
private ArrayAdapter<Action> adapterAction; private ArrayAdapter<Action> adapterAction;
private ArrayAdapter<EntityFolder> adapterTarget;
private long id = -1; private long id = -1;
@ -95,8 +95,7 @@ public class FragmentRule extends FragmentBase {
etSubject = view.findViewById(R.id.etSubject); etSubject = view.findViewById(R.id.etSubject);
etText = view.findViewById(R.id.etText); etText = view.findViewById(R.id.etText);
spAction = view.findViewById(R.id.spAction); spAction = view.findViewById(R.id.spAction);
spMove = view.findViewById(R.id.spMove); spTarget = view.findViewById(R.id.spTarget);
cbMoveSeen = view.findViewById(R.id.cbMoveSeen);
bottom_navigation = view.findViewById(R.id.bottom_navigation); bottom_navigation = view.findViewById(R.id.bottom_navigation);
pbWait = view.findViewById(R.id.pbWait); pbWait = view.findViewById(R.id.pbWait);
grpReady = view.findViewById(R.id.grpReady); grpReady = view.findViewById(R.id.grpReady);
@ -114,10 +113,14 @@ public class FragmentRule extends FragmentBase {
adapterAction.setDropDownViewResource(R.layout.spinner_item1_dropdown); adapterAction.setDropDownViewResource(R.layout.spinner_item1_dropdown);
spAction.setAdapter(adapterAction); spAction.setAdapter(adapterAction);
adapterTarget = new ArrayAdapter<>(getContext(), R.layout.spinner_item1, android.R.id.text1, new ArrayList<EntityFolder>());
adapterTarget.setDropDownViewResource(R.layout.spinner_item1_dropdown);
spTarget.setAdapter(adapterTarget);
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
actions.add(new Action(EntityRule.TYPE_SEEN, getString(R.string.title_seen))); actions.add(new Action(EntityRule.TYPE_SEEN, getString(R.string.title_seen)));
actions.add(new Action(EntityRule.TYPE_UNSEEN, getString(R.string.title_unseen))); actions.add(new Action(EntityRule.TYPE_UNSEEN, getString(R.string.title_unseen)));
//actions.add(new Action(EntityRule.TYPE_MOVE, getString(R.string.title_move))); actions.add(new Action(EntityRule.TYPE_MOVE, getString(R.string.title_move)));
adapterAction.addAll(actions); adapterAction.addAll(actions);
spAccount.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { spAccount.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@ -133,6 +136,23 @@ public class FragmentRule extends FragmentBase {
} }
}); });
spAction.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
Action action = (Action) adapterView.getAdapter().getItem(position);
onActionSelected(action.type);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
onActionSelected(-1);
}
private void onActionSelected(int type) {
grpMove.setVisibility(type == EntityRule.TYPE_MOVE ? View.VISIBLE : View.GONE);
}
});
bottom_navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { bottom_navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override @Override
public boolean onNavigationItemSelected(MenuItem menuItem) { public boolean onNavigationItemSelected(MenuItem menuItem) {
@ -223,6 +243,9 @@ public class FragmentRule extends FragmentBase {
spAccount.setTag(rule.account); spAccount.setTag(rule.account);
spFolder.setTag(rule.folder); spFolder.setTag(rule.folder);
if (type == EntityRule.TYPE_MOVE)
spTarget.setTag(jaction.getLong("target"));
for (int pos = 0; pos < adapterAccount.getCount(); pos++) for (int pos = 0; pos < adapterAccount.getCount(); pos++)
if (adapterAccount.getItem(pos).id.equals(rule.account)) { if (adapterAccount.getItem(pos).id.equals(rule.account)) {
spAccount.setSelection(pos); spAccount.setSelection(pos);
@ -265,6 +288,9 @@ public class FragmentRule extends FragmentBase {
adapterFolder.clear(); adapterFolder.clear();
adapterFolder.addAll(folders); adapterFolder.addAll(folders);
adapterTarget.clear();
adapterTarget.addAll(folders);
long account = args.getLong("account"); long account = args.getLong("account");
if (account == (Long) spAccount.getTag()) { if (account == (Long) spAccount.getTag()) {
Long folder = (Long) spFolder.getTag(); Long folder = (Long) spFolder.getTag();
@ -273,8 +299,17 @@ public class FragmentRule extends FragmentBase {
spFolder.setSelection(pos); spFolder.setSelection(pos);
break; break;
} }
} else
Long target = (Long) spTarget.getTag();
for (int pos = 0; pos < folders.size(); pos++)
if (folders.get(pos).id.equals(target)) {
spTarget.setSelection(pos);
break;
}
} else {
spFolder.setSelection(0); spFolder.setSelection(0);
spTarget.setSelection(0);
}
grpReady.setVisibility(View.VISIBLE); grpReady.setVisibility(View.VISIBLE);
bottom_navigation.setVisibility(View.VISIBLE); bottom_navigation.setVisibility(View.VISIBLE);
@ -352,8 +387,13 @@ public class FragmentRule extends FragmentBase {
Action action = (Action) spAction.getSelectedItem(); Action action = (Action) spAction.getSelectedItem();
JSONObject jaction = new JSONObject(); JSONObject jaction = new JSONObject();
if (action != null) if (action != null) {
jaction.put("type", action.type); jaction.put("type", action.type);
if (action.type == EntityRule.TYPE_MOVE) {
EntityFolder target = (EntityFolder) spTarget.getSelectedItem();
jaction.put("target", target.id);
}
}
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", id); args.putLong("id", id);

@ -88,9 +88,9 @@ public class FragmentRules extends FragmentBase {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
DB db = DB.getInstance(getContext()); DB db = DB.getInstance(getContext());
db.rule().liveRules().observe(getViewLifecycleOwner(), new Observer<List<EntityRule>>() { db.rule().liveRules().observe(getViewLifecycleOwner(), new Observer<List<TupleRuleEx>>() {
@Override @Override
public void onChanged(List<EntityRule> rules) { public void onChanged(List<TupleRuleEx> rules) {
if (rules == null) if (rules == null)
rules = new ArrayList<>(); rules = new ArrayList<>();

@ -1037,7 +1037,9 @@ public class ServiceSynchronize extends LifecycleService {
db.beginTransaction(); db.beginTransaction();
downloadMessage(ServiceSynchronize.this, downloadMessage(ServiceSynchronize.this,
folder, ifolder, (IMAPMessage) imessage, folder, ifolder, (IMAPMessage) imessage,
message.id, db.folder().getFolderDownload(folder.id)); message.id,
db.folder().getFolderDownload(folder.id),
db.rule().getEnabledRules(folder.id));
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();
@ -1124,7 +1126,9 @@ public class ServiceSynchronize extends LifecycleService {
db.beginTransaction(); db.beginTransaction();
downloadMessage(ServiceSynchronize.this, downloadMessage(ServiceSynchronize.this,
folder, ifolder, (IMAPMessage) e.getMessage(), folder, ifolder, (IMAPMessage) e.getMessage(),
message.id, db.folder().getFolderDownload(folder.id)); message.id,
db.folder().getFolderDownload(folder.id),
db.rule().getEnabledRules(folder.id));
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();
@ -2300,6 +2304,8 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderSyncState(folder.id, "downloading"); db.folder().setFolderSyncState(folder.id, "downloading");
List<EntityRule> rules = db.rule().getEnabledRules(folder.id);
//fp.add(IMAPFolder.FetchProfileItem.MESSAGE); //fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
// Download messages/attachments // Download messages/attachments
@ -2317,7 +2323,7 @@ public class ServiceSynchronize extends LifecycleService {
downloadMessage( downloadMessage(
this, this,
folder, ifolder, (IMAPMessage) isub[j], folder, ifolder, (IMAPMessage) isub[j],
ids[from + j], download); ids[from + j], download, rules);
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} catch (FolderClosedException ex) { } catch (FolderClosedException ex) {
throw ex; throw ex;
@ -2575,7 +2581,7 @@ public class ServiceSynchronize extends LifecycleService {
static void downloadMessage( static void downloadMessage(
Context context, Context context,
EntityFolder folder, IMAPFolder ifolder, IMAPMessage imessage, EntityFolder folder, IMAPFolder ifolder, IMAPMessage imessage,
long id, boolean download) throws MessagingException, IOException { long id, boolean download, List<EntityRule> rules) throws MessagingException, IOException {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id); EntityMessage message = db.message().getMessage(id);
if (message == null) if (message == null)
@ -2584,7 +2590,7 @@ public class ServiceSynchronize extends LifecycleService {
if (message.setContactInfo(context)) if (message.setContactInfo(context))
db.message().updateMessage(message); db.message().updateMessage(message);
if (download) { if (download || rules.size() > 0) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
long maxSize = prefs.getInt("download", 32768); long maxSize = prefs.getInt("download", 32768);
if (maxSize == 0) if (maxSize == 0)
@ -2624,7 +2630,7 @@ public class ServiceSynchronize extends LifecycleService {
MessageHelper.MessageParts parts = helper.getMessageParts(); MessageHelper.MessageParts parts = helper.getMessageParts();
if (!message.content) if (!message.content) {
if (!metered || (message.size != null && message.size < maxSize)) { if (!metered || (message.size != null && message.size < maxSize)) {
String body = parts.getHtml(context); String body = parts.getHtml(context);
message.write(context, body); message.write(context, body);
@ -2633,6 +2639,11 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(folder.name + " downloaded message id=" + message.id + " size=" + message.size); Log.i(folder.name + " downloaded message id=" + message.id + " size=" + message.size);
} }
for (EntityRule rule : rules)
if (rule.matches(context, message))
rule.execute(context, db, message);
}
for (int i = 0; i < attachments.size(); i++) { for (int i = 0; i < attachments.size(); i++) {
EntityAttachment attachment = attachments.get(i); EntityAttachment attachment = attachments.get(i);
if (!attachment.available) if (!attachment.available)

@ -21,13 +21,17 @@ package eu.faircode.email;
public class TupleRuleEx extends EntityRule { public class TupleRuleEx extends EntityRule {
public long account; public long account;
public String folderName;
public String accountName;
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof TupleRuleEx) { if (obj instanceof TupleRuleEx) {
TupleRuleEx other = (TupleRuleEx) obj; TupleRuleEx other = (TupleRuleEx) obj;
return (super.equals(obj) && return (super.equals(obj) &&
this.account == other.account); this.account == other.account &&
(this.folderName == null ? other.folderName == null : this.folderName.equals(other.folderName)) &&
(this.accountName == null ? other.accountName == null : this.accountName.equals(other.accountName)));
} else } else
return false; return false;
} }

@ -26,6 +26,7 @@ import com.sun.mail.imap.IMAPMessage;
import com.sun.mail.imap.IMAPStore; import com.sun.mail.imap.IMAPStore;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
@ -230,7 +231,8 @@ public class ViewModelBrowse extends ViewModel {
message = ServiceSynchronize.synchronizeMessage(state.context, message = ServiceSynchronize.synchronizeMessage(state.context,
folder, state.ifolder, (IMAPMessage) isub[j], true); folder, state.ifolder, (IMAPMessage) isub[j], true);
ServiceSynchronize.downloadMessage(state.context, ServiceSynchronize.downloadMessage(state.context,
folder, state.ifolder, (IMAPMessage) isub[j], message.id, false); folder, state.ifolder, (IMAPMessage) isub[j], message.id,
false, new ArrayList<EntityRule>());
count++; count++;
} }
db.message().setMessageFound(message.account, message.thread); db.message().setMessageFound(message.account, message.thread);

@ -198,7 +198,7 @@
app:layout_constraintTop_toBottomOf="@id/tvAction" /> app:layout_constraintTop_toBottomOf="@id/tvAction" />
<TextView <TextView
android:id="@+id/tvMove" android:id="@+id/tvTarget"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
@ -208,33 +208,24 @@
app:layout_constraintTop_toBottomOf="@id/spAction" /> app:layout_constraintTop_toBottomOf="@id/spAction" />
<Spinner <Spinner
android:id="@+id/spMove" android:id="@+id/spTarget"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvMove" /> app:layout_constraintTop_toBottomOf="@id/tvTarget" />
<CheckBox
android:id="@+id/cbMoveSeen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_rule_seen"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/spMove" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpReady" android:id="@+id/grpReady"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:constraint_referenced_ids="tvName,etName,tvAccount,spAccount,tvFolder,spFolder,tvFolderRemark,tvOrder,etOrder,spFolder,tvSender,etSender,tvSubject,etSubject,tvText,etText,tvAction,spAction" /> app:constraint_referenced_ids="tvName,etName,tvOrder,etOrder,cbEnabled,tvAccount,spAccount,tvFolder,spFolder,tvFolderRemark,tvSender,etSender,tvSubject,etSubject,tvText,etText,tvAction,spAction,tvTarget,spTarget" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpMove" android:id="@+id/grpMove"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:constraint_referenced_ids="tvMove,spMove,cbMoveSeen" /> app:constraint_referenced_ids="tvTarget,spTarget" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>

@ -25,6 +25,22 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvFolder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:gravity="end"
android:maxLines="1"
android:text="Name"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvName" />
<View <View
android:id="@+id/vSeparator" android:id="@+id/vSeparator"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -33,6 +49,6 @@
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:background="?attr/colorSeparator" android:background="?attr/colorSeparator"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvName" /> app:layout_constraintTop_toBottomOf="@id/tvFolder" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout> </FrameLayout>
Loading…
Cancel
Save